654 字
3 分钟
Steam 家庭共享库预览 - 服务端设计与实现
在这篇文章中,我将介绍如何设计和实现一个基于 Node.js 和 Express 框架的服务端项目,提供 Steam 家庭共享库的预览功能。我们将聚焦于一个名为 gamers 的接口,该接口接收 ids 参数,并返回对应的游戏信息。
技术栈选择
在构建服务端项目时,选择合适的技术栈是成功的关键。对于本项目,我们选择了 Node.js 和 Express 框架。选择这两者的原因如下:
- Node.js:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,具有非阻塞、事件驱动的特点,非常适合构建高并发的应用程序。
- 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, }); }});
接口测试
- 无入参
- 随机输入的 steamid
- 真实存在的 steamid
Steam 家庭共享库预览 - 服务端设计与实现
https://www.mihouo.com/posts/front/steamshared/server-side-design-and-implementation/