随心而记
654 字
3 分钟
Steam 家庭共享库预览 - 服务端设计与实现
2024-05-31
发布时间
分类
标签

在这篇文章中,我将介绍如何设计和实现一个基于 Node.js 和 Express 框架的服务端项目,提供 Steam 家庭共享库的预览功能。我们将聚焦于一个名为 gamers 的接口,该接口接收 ids 参数,并返回对应的游戏信息。

技术栈选择

在构建服务端项目时,选择合适的技术栈是成功的关键。对于本项目,我们选择了 Node.js 和 Express 框架。选择这两者的原因如下:

  1. Node.js:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,具有非阻塞、事件驱动的特点,非常适合构建高并发的应用程序。
  2. Express:Express 是一个快速、简洁的 Node.js Web 应用框架,提供了一系列强大的功能来帮助开发者快速构建 Web 应用和 API。

接口设计#

主要用到的 Steam Web API 接口如下

• 获取用户信息: http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/

• 获取用户拥有的游戏: http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/

• 获取游戏详细信息: http://store.steampowered.com/api/appdetails

接口实现#

app.post('/get/gamers', async (req, res) => {
  try {
    const { ids, key: reqKey = '' } = req.body;
    if (!ids || ids?.length === 0) {
      res.send({
        code: 400,
        data: null,
        msg: '请输入用户 id',
        success: false,
      });
    }
    const key = reqKey || await getKey();
    if (!key) {
      res.send({
        code: 500,
        data: null,
        msg: '无可用key',
        success: false,
      });
    }
    const resPlayerSummaries = await service({
      url: '/ISteamUser/GetPlayerSummaries/v0002/',
      params: {
        steamids: ids.join(','),
        format: 'json',
        key,
      },
      instance: 'IPlayerService',
      method: 'get',
    }).then(res => {
      if (res.response.players) {
        return res.response.players;
      }
      return [];
    });
    if (resPlayerSummaries.length === 0) {
      res.send({
        code: 400,
        data: null,
        msg: '输入的用户 id 均无效',
        success: false,
      })
    }
    const resAllGames= await Promise.allSettled(resPlayerSummaries.map(player => {
      if (player.communityvisibilitystate === 1) {
        return Promise.resolve({games: [], steamid: player.steamid});
      }
      return service({
        url: '/IPlayerService/GetOwnedGames/v0001/',
        params: {
          steamid: player.steamid, // 76561198391051438
          format: 'json',
          key,
        },
        instance: 'IPlayerService',
      }).then(res => {
        if (res.response?.games) {
          return {games: res.response.games, steamid: player.steamid};
        }
        return {games: [], steamid: player.steamid};
      })
    })).then(res => {
      return res.map(res => res.value);
    });
    const allGamesIds = [...new Set(...[resAllGames.map(res => res?.games.map(game => game.appid))].map(ids => ids.flat()))];
    const resGames = await Promise.allSettled(allGamesIds.map(appid => {
      return service({
        url: '/api/appdetails?appids',
        params: {
          appids: appid,
          l: 'schinese',
          cc: 'cn',
          format: 'json',
          filters: 'basic,price_overview,categories',
          key,
        },
        instance: 'Storefront',
      }).then(res => {
        const data = Object.values(res)[0];
        if (data.success) {
          return data.data;
        }
        return {
          steam_appid: appid,
          success: false,
        };
      }).catch(err => {
        return {
          steam_appid: appid,
          success: false,
        };
      });
    }));
    const successGames = resGames.filter(game => (game.value?.success ?? true));
    const failGames = resGames.filter(game => !(game.value?.success ?? true));
    const result = [];
    ids.forEach((id, index) => {
      const cur = resPlayerSummaries.find(player => player.steamid === id);
      if (!cur) {
        return;
      }
      if (cur.communityvisibilitystate === 1) {
        result.push({
          isPublic: false,
          user: cur,
          stock: [],
          failGameId: [],
        });
        return;
      }
      const curUserGameIds = resAllGames.find(res => res?.steamid === id)?.games.map(game => game.appid);
      result.push({
        isPublic: true,
        user: cur,
        stock: successGames.filter(game => curUserGameIds?.includes(game.value.steam_appid)).map(game => game.value),
        failGameId: failGames.filter(game => curUserGameIds?.includes(game.value.steam_appid)).map(game => game.value.steam_appid),
      });
    });
    res.send({
      code: 200,
      data: result,
      msg: 'success',
      success: true,
    });
  } catch (e) {
    console.log(e);
    res.send({
      code: 500,
      msg: e === '无可用key' ? e : '服务器错误',
      success: false,
    });
  }
});

接口测试#

  1. 无入参

Untitled

  1. 随机输入的 steamid

Untitled

  1. 真实存在的 steamid

Untitled

Untitled

Steam 家庭共享库预览 - 服务端设计与实现
https://www.promises.top/posts/steamshared/server-side-design-and-implementation/
作者
发布于
2024-05-31
许可协议
CC BY-NC-SA 4.0