From 31b4764cd5233acb25f4d0027261fe83eaad6ec7 Mon Sep 17 00:00:00 2001 From: schneefux Date: Mon, 8 May 2017 23:15:33 +0200 Subject: rewrite the whole thing (wip) --- views/guild.js | 45 +++++++++++++++++++++ views/guild_add.js | 34 ++++++++++++++++ views/guild_create.js | 48 ++++++++++++++++++++++ views/match.js | 58 +++++++++++++++++++++++++++ views/matches.js | 81 +++++++++++++++++++++++++++++++++++++ views/player.js | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ views/register.js | 41 +++++++++++++++++++ views/view.js | 39 ++++++++++++++++++ 8 files changed, 454 insertions(+) create mode 100644 views/guild.js create mode 100644 views/guild_add.js create mode 100644 views/guild_create.js create mode 100644 views/match.js create mode 100644 views/matches.js create mode 100644 views/player.js create mode 100644 views/register.js create mode 100644 views/view.js (limited to 'views') diff --git a/views/guild.js b/views/guild.js new file mode 100644 index 0000000..1a4cb78 --- /dev/null +++ b/views/guild.js @@ -0,0 +1,45 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildOverviewView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, user_token) { + super(msg); + this.user_token = user_token; + } + + async text(members) { + // TODO remove when API supports order by fame + members = members.sort((m1, m2) => m1.fame < m2.fame); + return members.map((member) => + `${member.player.name} ${member.fame}`).join("\n"); + } + + async embed(guild) { + const embed = util.vainsocialEmbed(`${guild.name} - ${guild.shard_id}`, + "", "vainsocial-guild-view") + .setDescription(await this.text(guild.members)); + return embed; + }; + + async respond() { + const guild = await api.getGuild(this.user_token); + if (guild == undefined) { + this.response = await util.respond(this.msg, + strings.notRegistered, this.response); + return this.response; + } + this.response = await util.respond(this.msg, + await this.embed(guild), this.response); + return this.response; + }; +} diff --git a/views/guild_add.js b/views/guild_add.js new file mode 100644 index 0000000..cb32dc3 --- /dev/null +++ b/views/guild_add.js @@ -0,0 +1,34 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildAddView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, players) { + super(msg); + this.players = players; + } + + // players: obj, key=ign, value=player + async text(players) { + return Object.entries(players).map((tuple) => + (tuple[1] == undefined)? + `Loading ${tuple[0]}…` + : `Loaded ${tuple[0]}.` + ).join("\n"); + } + + async respond() { + this.response = await util.respond(this.msg, + await this.text(this.players), this.response); + return this.response; + }; +} diff --git a/views/guild_create.js b/views/guild_create.js new file mode 100644 index 0000000..9720191 --- /dev/null +++ b/views/guild_create.js @@ -0,0 +1,48 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildCreateView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, user_token) { + super(msg); + this.user_token = user_token; + } + + async text() { + return `Guild created. You can now use ${util.usg(this.msg, "vgadd ign1 ign2 ignN")} to add members to your Guild.`; + } + + async help() { + return `*${emoji.symbols.information_source} or ${util.usg(this.msg, "vgview")} to view your Guild*` + } + + async buttons() { + let reactions = {}; + reactions[emoji.symbols.information_source] = async () => { + util.trackAction(this.msg, "reaction-guildview"); + await new GuildOverviewView(this.user_token).respond(); + }; + return reactions; + } + + // TODO move to super class + async respond() { + this.response = await util.respond(this.msg, + await this.text(), this.response); + if (!this.hasButtons) { + await util.reactionButtons(this.response, + await this.buttons()); + this.hasButtons = true; + } + return this.response; + }; +} diff --git a/views/match.js b/views/match.js new file mode 100644 index 0000000..8a04045 --- /dev/null +++ b/views/match.js @@ -0,0 +1,58 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const PREVIEW = process.env.PREVIEW != "false", + ROOTURL = (PREVIEW? "https://preview.vainsocial.com/":"https://vainsocial.com/"); + +const MatchView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, matchid) { + super(msg); + this.matchid = matchid; + } + + // return [[title, text], …] for rosters + static async text(match) { + let resps = []; + for(let roster of match.rosters) { + let winstr = "Won"; + if (!roster.winner) winstr = "Lost"; + let rosterstr = `${roster.side} - \`${roster.hero_kills}\` Kills - ${winstr}`; + let teamstr = ""; + for(let participant of roster.participants) { + const hero = await api.mapActor(participant.actor), + emojiScore = strings.emojifyScore(participant.stats.impact_score); + teamstr += ` +\`${hero}\`, [${participant.player.name}](${ROOTURL}player/${participant.player.name}${util.track("match-detail")}) \`T${Math.floor(participant.skill_tier/3+1)}\` | \`${participant.stats.kills}/${participant.stats.deaths}/${participant.stats.assists}\`, \`${Math.floor(participant.stats.farm)}\`, Score ${emojiScore} \`${Math.floor(100 * participant.stats.impact_score)}%\``; + } + resps.push([rosterstr, teamstr]); + } + return resps; + } + + async embed(match) { + let embed = util.vainsocialEmbed(`${match.game_mode}, ${match.duration} minutes`, + "matches/" + match.api_id , "vainsocial-match") + .setTimestamp(new Date(match.created_at)); + (await this.text(match)).forEach(([title, text]) => { + embed.addField(title, text, true); + }); + return embed; + }; + + async respond() { + const match = await api.getMatch(this.matchid); + this.response = await util.respond(this.msg, + await this.embed(match), this.response); + return this.response; + }; +} diff --git a/views/matches.js b/views/matches.js new file mode 100644 index 0000000..279bf9c --- /dev/null +++ b/views/matches.js @@ -0,0 +1,81 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const emoji = require("discord-emoji"), + Promise = require("bluebird"), + View = require("./view"), + MatchView = require("./match"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"); + +const MATCH_HISTORY_LEN = parseInt(process.env.MATCH_HISTORY_LEN) || 3; + +const MatchesView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, ign) { + super(msg); + this.ign = ign; + } + + async text(participant) { + let winstr = "Won", + hero = await api.mapActor(participant.actor), + game_mode = await api.mapGameMode(participant.game_mode_id), + emojiScore = strings.emojifyScore(participant.stats.impact_score); + if (!participant.winner) winstr = "Lost"; + return ` +${winstr} ${game_mode} with \`${hero}\` +KDA, CS | \`${participant.stats.kills}/${participant.stats.deaths}/${participant.stats.assists}\`, \`${Math.round(participant.stats.farm)}\` +Kill Participation | \`${Math.floor(100 * participant.stats.kill_participation)}%\` +Score | ${emojiScore} \`${Math.floor(100 * participant.stats.impact_score)}%\` +`; + } + + async embed(matches) { + const matchesPart = matches.slice(0, MATCH_HISTORY_LEN); + + // build embed + let embed = util.vainsocialEmbed( + this.ign, "player/" + this.ign, "vainsocial-matches") + .setDescription(`Last ${matchesPart.length} casual and ranked matches.\n` + + await this.help()) + .setTimestamp(new Date(matchesPart[0].created_at)); + await Promise.each(matchesPart, async (match, idx) => + embed.addField(`Match ${idx + 1}`, await this.text(match)) + ); + return embed; + } + + async help() { + return `*${emoji.symbols["1234"]} or ${util.usg(this.msg, "vm " + this.ign + " number")} for details*` + } + + async buttons(matches) { + const matchesPart = matches.slice(0, MATCH_HISTORY_LEN); + + let reactions = {}; + matchesPart.forEach((m, idx) => + reactions[strings.emojiCount[idx]] = async () => { + console.log("react"); + util.trackAction(this.msg, "reaction-match", m.match_api_id); + await new MatchView(this.msg, m.match_api_id).respond(); + }); + return reactions; + } + + async respond() { + const matches = await api.getMatches(this.ign); + this.response = await util.respond(this.msg, + await this.embed(matches), this.response); + if (!this.hasButtons) { + await util.reactionButtons(this.response, + await this.buttons(matches), this.msg); + this.hasButtons = true; + } + return this.response; + } +} diff --git a/views/player.js b/views/player.js new file mode 100644 index 0000000..ec5d38b --- /dev/null +++ b/views/player.js @@ -0,0 +1,108 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const emoji = require("discord-emoji"), + View = require("./view"), + MatchView = require("./match"), + MatchesView = require("./matches"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const PREVIEW = process.env.PREVIEW != "false", + ROOTURL = (PREVIEW? "https://preview.vainsocial.com/":"https://vainsocial.com/"); + +const PlayerView = module.exports; + +// player profile +module.exports = class extends View { + constructor(msg, ign) { + super(msg); + this.ign = ign; + } + + async text(player) { + const stats = oneLine` + Win Rate | \`${Math.round(100 * + player.currentSeries.reduce((t, s) => t + s.wins, 0) / + player.currentSeries.reduce((t, s) => t + s.played, 0) + )}%\` + `, + total_kda = oneLine` + Total KDA | \`${player.stats.kills}\` / \`${player.stats.deaths}\` / \`${player.stats.assists}\` + `; + let best_hero = "", + picks = ""; + if (player.best_hero.length > 0) + best_hero = oneLine` + Best | \`${player.best_hero[0].name}\` + `; + if (player.picks.length > 0) { + const hero = await api.mapActor(player.picks[0].actor); + picks = oneLine`Favorite | \`${hero}\`, \`${player.picks[0].hero_pick} picks\``; + } + return ` +${stats} +${total_kda} +${best_hero} +${picks} + `; + } + + async embed(player, matches) { + return util.vainsocialEmbed(`${player.name} - ${player.shard_id}`, "player/" + player.name, "vainsocial-user") + .setThumbnail(ROOTURL + "images/game/skill_tiers/" + + matches[0].skill_tier + ".png") + .setDescription("") + .addField(strings.profile, await this.text(player), true) + .addField(strings.lastMatch, await new MatchesView().text(matches[0]) + + "\n" + await this.help(), true) + .setTimestamp(new Date(matches[0].created_at)); + }; + + async help() { + return oneLine` +*${emoji.symbols.information_source} or ${util.usg(this.msg, "vm " + this.ign)} for detail, +${emoji.symbols["1234"]} or ${util.usg(this.msg, "vh " + this.ign)} for more*`; + } + + async buttons(player, matches) { + let reactions = {}; + reactions[emoji.symbols.information_source] = async () => { + util.trackAction(this.msg, "reaction-match", player.name); + await new MatchView(this.msg, matches[0].match_api_id).respond(); + }; + reactions[emoji.symbols["1234"]] = async () => { + util.trackAction(this.msg, "reaction-matches", player.name); + await new MatchesView(this.msg, player.name).respond(); + }; + return reactions; + } + + async respond() { + const [player, matches] = await Promise.all([ + api.getPlayer(this.ign), + api.getMatches(this.ign) + ]); + if (player == undefined) { + this.response = await util.respond(this.msg, + strings.loading(this.ign), this.response); + return this.response; + } + if (matches.length == 0) { + this.response = await util.respond(this.msg, + strings.loading(this.ign), this.response); + return this.response; + } + this.response = await util.respond(this.msg, + await this.embed(player, matches), this.response); + if (!this.hasButtons) { + await util.reactionButtons(this.response, + await this.buttons(player, matches)); + this.hasButtons = true; + } + return this.response; + } +} diff --git a/views/register.js b/views/register.js new file mode 100644 index 0000000..0e5ff71 --- /dev/null +++ b/views/register.js @@ -0,0 +1,41 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const RegisterView = module.exports; + +// user register view +module.exports = class extends View { + async text() { + return `You are now registered at VainSocial, ${this.msg.author.mention}.`; + } + async help() { + return `*${emoji.symbols.repeat} or ${util.usg(this.msg, "v")} to view your profile, ${util.usg(this.msg, "vgcreate")} to create a Guild*` + } + async buttons() { + let reactions = {}; + reactions[emoji.symbols.repeat] = async () => { + util.trackAction(this.msg, "reaction-player"); + const ign = await util.ignForUser(undefined, this.msg.author.id); + await new PlayerView(this.msg, ign).respond(); + }; + return reactions; + } + async respond() { + await api.setUser(msg.author.id, ign); + this.response = await util.respond(this.msg, + await this.text(), this.response); + if (!this.hasButtons) { + await util.reactionButtons(this.response, + await this.buttons(player, matches)); + this.hasButtons = true; + } + return this.response; + }; +} diff --git a/views/view.js b/views/view.js new file mode 100644 index 0000000..3d3ae38 --- /dev/null +++ b/views/view.js @@ -0,0 +1,39 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const util = require("../util"); + +module.exports = class { + constructor(msg) { + // command message + this.msg = msg; + // response (d'oh) + this.response = undefined; + this.hasButtons = false; + } + static async text() { + // return the markdown or an array of [title, markdown] + return ""; + } + async embed() { + // return an embed using `text()` and `help()` + return util.vainsocialEmbed("title", "url"); + } + async help() { + // return explanation for buttons + return ""; + } + async buttons() { + // return buttons pointing to different views + // key: emoji, value: async func + return {}; + } + async respond() { + // reply with embed + buttons + this.response = await util.respond(this.msg, + await this.embed(), this.response); + await util.reactionButtons(this.response, await this.buttons(), this.msg); + return this.response; + } +}; -- cgit v1.3.1 From 54b59d1168e79c8706b164194bc1a48ec49726e4 Mon Sep 17 00:00:00 2001 From: schneefux Date: Tue, 9 May 2017 13:24:41 +0200 Subject: error handling --- api.js | 56 +++++++++++++++++++--------------- commands/vainsocial/guild_add.js | 3 +- commands/vainsocial/guild_fame_calc.js | 36 ---------------------- commands/vainsocial/guild_update.js | 28 +++++++++++------ commands/vainsocial/match.js | 32 +++++++++++-------- commands/vainsocial/matches.js | 22 ++++++++----- commands/vainsocial/me.js | 16 +++++++--- commands/vainsocial/user.js | 23 +++++++++----- util.js | 10 ++++-- views/guild_add.js | 34 --------------------- views/match.js | 11 ++----- views/register.js | 16 +++++++--- views/view.js | 10 +++++- 13 files changed, 144 insertions(+), 153 deletions(-) delete mode 100644 commands/vainsocial/guild_fame_calc.js delete mode 100644 views/guild_add.js (limited to 'views') diff --git a/api.js b/api.js index d303cab..74614c2 100644 --- a/api.js +++ b/api.js @@ -43,19 +43,12 @@ module.exports.getMap = async (url) => { module.exports.getFE = module.exports.get = async (url, params={}, ttl=60, cachekey=undefined) => { if (cachekey == undefined) cachekey = url + JSON.stringify(params); - return await cache.wrap(cachekey, async () => { - try { - return await request({ - uri: API_FE_URL + url, - qs: params, - json: true, - forever: true - }); - } catch (err) { - // TODO sort errors, loggly - return undefined; - } - }, { ttl: ttl }); + return await cache.wrap(cachekey, async () => await request({ + uri: API_FE_URL + url, + qs: params, + json: true, + forever: true + }), { ttl: ttl }); } // send a POST and optionally bust cache @@ -139,7 +132,13 @@ module.exports.subscribeUpdates = (name, timeout=UPDATE_TIMEOUT) => { cache.del("matches+" + name); cache.del("player+" + name); } - if ([Channel.DONE, "search_fail"].indexOf(msg) != -1) { + if (msg == "search_fail") { + subscription.unsubscribe(); + throw { error: { + err: "No player found for the provided IGN." + } }; + } + if (msg == Channel.DONE) { subscription.unsubscribe(); return undefined; } @@ -156,14 +155,18 @@ module.exports.updatePlayer = (name) => api.postBE("/player/" + name + "/update"); // return player -module.exports.getPlayer = (name) => - api.getFE("/player/" + name, {}, 60, "player+" + name); +module.exports.getPlayer = async (name) => { + return await api.getFE("/player/" + name, {}, 60, "player+" + name); +} // search or update a player module.exports.upsearchPlayer = async (name) => { - if (await api.getPlayer(name) == undefined) + try { + await api.getPlayer(name); + await api.updatePlayer(name); + } catch (err) { await api.searchPlayer(name); - else await api.updatePlayer(name); + } } // block until update is completely done @@ -181,17 +184,18 @@ module.exports.upsearchPlayerSync = async (name) => { module.exports.getMatches = async (name) => { const data = await api.getFE("/player/" + name + "/matches/1.1.1.1", {}, 60 * 60, "matches+" + name); - if (data == undefined) return []; return data[0].data; } // return single match -module.exports.getMatch = async (id) => - await api.getFE("/match/" + id, {}, 60 * 60); +module.exports.getMatch = async (id) => { + return await api.getFE("/match/" + id, {}, 60 * 60); +} // return a guild -module.exports.getGuild = (token) => - api.getFE("/guild", { user_token: token }, 60, "guild+" + token); +module.exports.getGuild = async (token) => { + return await api.getFE("/guild", { user_token: token }, 60, "guild+" + token); +} // TODO! cache guilds by guild id, not by user token // add user to guild @@ -231,5 +235,7 @@ module.exports.setUser = async (token, name) => { } // retrieve Discord ID -> IGN -module.exports.getUser = async (token) => - (await api.getFE("/user", { user_token: token }, 60, "user+" + token)).name; +module.exports.getUser = async (token) => { + const user = await api.getFE("/user", { user_token: token }, 60, "user+" + token); + return user.name; +} diff --git a/commands/vainsocial/guild_add.js b/commands/vainsocial/guild_add.js index 0021360..8474724 100644 --- a/commands/vainsocial/guild_add.js +++ b/commands/vainsocial/guild_add.js @@ -7,7 +7,7 @@ const Commando = require("discord.js-commando"), Promise = require("bluebird"), api = require("../../api"), util = require("../../util"), - GuildAddView = require("../../views/guild_add"); + GuildAddView = require("../../views/guild_progress"); module.exports = class AddGuildMemberCommand extends Commando.Command { constructor(client) { @@ -24,7 +24,6 @@ Register IGNs to your Guild. argsType: "multiple" }); } - // register a VainSocial Guild to a Discord account async run(msg, args) { util.trackAction(msg, "vainsocial-guild-add"); let playersData = {}; diff --git a/commands/vainsocial/guild_fame_calc.js b/commands/vainsocial/guild_fame_calc.js deleted file mode 100644 index e1ec912..0000000 --- a/commands/vainsocial/guild_fame_calc.js +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/node -/* jshint esnext:true */ -"use strict"; - -const Commando = require("discord.js-commando"), - oneLine = require("common-tags").oneLine, - api = require("../../api"), - util = require("../../util"); - -module.exports = class CalculateGuildFameCommand extends Commando.Command { - constructor(client) { - super(client, { - name: "vainsocial-guildcalc", - aliases: ["vguild-calc", "vgcalc"], - group: "vainsocial-guild", - memberName: "vainsocial-guildcalc", - description: "Update your Guild's fame.", - details: oneLine` -Recalculate your Guild members' fame. -`, - examples: ["vgcalc"] - }); - } - // internal / premium: immediately call backend fame refresh - async run(msg, args) { - util.trackAction(msg, "vainsocial-guild-calculate"); - const guild = await api.getGuild(msg.author.id); - if (guild == undefined) { - await msg.reply("You are not registered in any guilds."); - return; - } - await msg.reply("Your Guild members' fame will be updated soon…"); - await api.calculateGuild(guild.id, msg.author.id); - await msg.reply("Your Guild members' fame has been updated."); - } -}; diff --git a/commands/vainsocial/guild_update.js b/commands/vainsocial/guild_update.js index 9c9fe1d..66e8a5f 100644 --- a/commands/vainsocial/guild_update.js +++ b/commands/vainsocial/guild_update.js @@ -6,7 +6,8 @@ const Commando = require("discord.js-commando"), oneLine = require("common-tags").oneLine, Promise = require("bluebird"), api = require("../../api"), - util = require("../../util"); + util = require("../../util"), + GuildMembersProgressView = require("../../views/guild_progress"); module.exports = class UpdateGuildCommand extends Commando.Command { constructor(client) { @@ -25,14 +26,21 @@ Update the match history for all your Guild members. // internal / premium: immediately call backend player refresh async run(msg, args) { util.trackAction(msg, "vainsocial-guild-update"); - const guild = await api.getGuild(msg.author.id); - if (guild == undefined) { - await msg.reply("You are not registered in any guilds."); - return; - } - // TODO progress report - await Promise.map(guild.members, - (member) => api.upsearchPlayer(member.player.name)); - await msg.reply("Your Guild members will be up to date soon."); + let playersData = {}; + const playersWaiters = args.map((name) => api.subscribeUpdates(name)), + guildUpdateView = new GuildMembersProgressView(msg, playersData); + // create waiter dict & data dict + await Promise.map(playersWaiters, async (waiter, idx) => { + await api.updatePlayer(args[idx]); + let success = false; + while (await waiter.next() != undefined) { + playersData[args[idx]] = await api.getPlayer(args[idx]); + await guildUpdateView.respond(); + success = true; + } + }); + await guildUpdateView.respond("Your Guild's fame is being updated…"); + await api.calculateGuild(guild.id, msg.author.id); + await guildUpdateView.respond("Your Guild was updated."); } }; diff --git a/commands/vainsocial/match.js b/commands/vainsocial/match.js index c3d2906..3acde84 100644 --- a/commands/vainsocial/match.js +++ b/commands/vainsocial/match.js @@ -42,19 +42,27 @@ module.exports = class ShowMatchCommand extends Commando.Command { } async run(msg, args) { util.trackAction(msg, "vainsocial-match", args.name); - const ign = await util.ignForUser(args.name, msg.author.id); - if (ign == undefined) return await msg.say(strings.unknown(msg)); + let ign; + try { + ign = await util.ignForUser(args.name, msg.author.id); + } catch (err) { + return await new MatchView(msg, undefined).error(strings.unknown(msg)); + } - const participations = await api.getMatches(ign); - if (args.number > participations.length) - return await msg.say(strings.tooFewMatches(ign)); - const matchView = new MatchView(msg, - participations[args.number-1].match_api_id); - // wait for BE update - const waiter = api.subscribeUpdates(ign); - await api.upsearchPlayer(ign); + const matchView = new MatchView(msg); + try { + const participations = await api.getMatches(ign); + if (args.number > participations.length) + return await msg.say(strings.tooFewMatches(ign)); + // wait for BE update + const waiter = api.subscribeUpdates(ign); + await api.upsearchPlayer(ign); - do await matchView.respond(); - while (await waiter.next() != undefined); + do await matchView.respond(participations[args.number-1].match_api_id); + while (await waiter.next() != undefined); + } catch (err) { + console.error(err); + return await matchView.error(err.error.err); + } } }; diff --git a/commands/vainsocial/matches.js b/commands/vainsocial/matches.js index 0197b8b..7499725 100644 --- a/commands/vainsocial/matches.js +++ b/commands/vainsocial/matches.js @@ -33,16 +33,24 @@ module.exports = class ShowMatchesCommand extends Commando.Command { } async run(msg, args) { util.trackAction(msg, "vainsocial-matches", args.name); - const ign = await util.ignForUser(args.name, msg.author.id); - if (ign == undefined) return await strings.unknown(msg); + let ign; + try { + ign = await util.ignForUser(args.name, msg.author.id); + } catch (err) { + return await strings.unknown(msg); + } - // peek const matchesView = new MatchesView(msg, ign); // wait for BE update - const waiter = api.subscribeUpdates(ign); - await api.upsearchPlayer(ign); + try { + const waiter = api.subscribeUpdates(ign); + await api.upsearchPlayer(ign); - do await matchesView.respond(); - while (await waiter.next() != undefined); + do await matchesView.respond(); + while (await waiter.next() != undefined); + } catch (err) { + console.log(err); + return await matchesView.error(err.error.err); + } } }; diff --git a/commands/vainsocial/me.js b/commands/vainsocial/me.js index e17afea..d2afb24 100644 --- a/commands/vainsocial/me.js +++ b/commands/vainsocial/me.js @@ -5,7 +5,8 @@ const Commando = require("discord.js-commando"), oneLine = require("common-tags").oneLine, api = require("../../api"), - util = require("../../util"); + util = require("../../util"), + RegisterView = require("../../views/register"); module.exports = class RegisterUserCommand extends Commando.Command { constructor(client) { @@ -33,8 +34,15 @@ Store your in game name for quicker access to other commands and for Guild manag // register a Discord account at VainSocial async run(msg, args) { util.trackAction(msg, "vainsocial-me", args.name); - await api.upsearchPlayer(ign); - await api.subscribeUpdates(ign).next(); - await new RegisterView(msg).respond(); + const registerView = new RegisterView(msg, args.name); + await api.upsearchPlayer(args.name); + try { + await api.subscribeUpdates(args.name).next(); + await api.setUser(msg.author.id, args.name); + } catch (err) { + console.log(err); + return await registerView.error(err.error.err); + } + await registerView.respond(); } }; diff --git a/commands/vainsocial/user.js b/commands/vainsocial/user.js index bfcc1a0..e251ad7 100644 --- a/commands/vainsocial/user.js +++ b/commands/vainsocial/user.js @@ -36,15 +36,24 @@ Display VainSocial lifetime statistics from Vainglory } async run(msg, args) { util.trackAction(msg, "vainsocial-user", args.name); - const ign = await util.ignForUser(args.name, msg.author.id); - if (ign == undefined) return await msg.say(strings.unknown(msg)); + let ign; + try { + ign = await util.ignForUser(args.name, msg.author.id); + } catch (err) { + return await new PlayerView(msg, args.name).error(strings.unknown(msg)); + } const playerView = new PlayerView(msg, ign); - // wait for BE update - const waiter = api.subscribeUpdates(ign); - await api.upsearchPlayer(ign); + try { + // wait for BE update + const waiter = api.subscribeUpdates(ign); + await api.upsearchPlayer(ign); - do await playerView.respond(); - while (await waiter.next() != undefined); + do await playerView.respond(); + while (await waiter.next() != undefined); + } catch (err) { + console.log(err); + return await playerView.error(err.error.err); + } } }; diff --git a/util.js b/util.js index a20cbaa..0f37918 100644 --- a/util.js +++ b/util.js @@ -142,6 +142,12 @@ module.exports.paginate = function* chunks(arr, pagesize) { } // return ign, or associated ign, or undefined -module.exports.ignForUser = async (name, user_token) => +module.exports.ignForUser = async (name, user_token) => { // "?" is not accepted as user input, but the default for empty args - (name != "?")? name : await api.getUser(user_token); + if (name != "?") return name; + try { + return await api.getUser(user_token); + } catch (err) { + return undefined; + } +} diff --git a/views/guild_add.js b/views/guild_add.js deleted file mode 100644 index cb32dc3..0000000 --- a/views/guild_add.js +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/node -/* jshint esnext:true */ -"use strict"; - -const View = require("./view"), - util = require("../util"), - api = require("../api"), - strings = require("../strings"), - oneLine = require("common-tags").oneLine; - -const GuildAddView = module.exports; - -// match detail view -module.exports = class extends View { - constructor(msg, players) { - super(msg); - this.players = players; - } - - // players: obj, key=ign, value=player - async text(players) { - return Object.entries(players).map((tuple) => - (tuple[1] == undefined)? - `Loading ${tuple[0]}…` - : `Loaded ${tuple[0]}.` - ).join("\n"); - } - - async respond() { - this.response = await util.respond(this.msg, - await this.text(this.players), this.response); - return this.response; - }; -} diff --git a/views/match.js b/views/match.js index 8a04045..2dc55ec 100644 --- a/views/match.js +++ b/views/match.js @@ -15,13 +15,8 @@ const MatchView = module.exports; // match detail view module.exports = class extends View { - constructor(msg, matchid) { - super(msg); - this.matchid = matchid; - } - // return [[title, text], …] for rosters - static async text(match) { + async text(match) { let resps = []; for(let roster of match.rosters) { let winstr = "Won"; @@ -49,8 +44,8 @@ module.exports = class extends View { return embed; }; - async respond() { - const match = await api.getMatch(this.matchid); + async respond(matchid) { + const match = await api.getMatch(matchid); this.response = await util.respond(this.msg, await this.embed(match), this.response); return this.response; diff --git a/views/register.js b/views/register.js index 0e5ff71..9943299 100644 --- a/views/register.js +++ b/views/register.js @@ -2,7 +2,8 @@ /* jshint esnext:true */ "use strict"; -const View = require("./view"), +const emoji = require("discord-emoji"), + View = require("./view"), util = require("../util"), api = require("../api"), strings = require("../strings"), @@ -12,6 +13,11 @@ const RegisterView = module.exports; // user register view module.exports = class extends View { + constructor(msg, ign) { + super(msg); + this.ign = ign; + } + async text() { return `You are now registered at VainSocial, ${this.msg.author.mention}.`; } @@ -22,18 +28,18 @@ module.exports = class extends View { let reactions = {}; reactions[emoji.symbols.repeat] = async () => { util.trackAction(this.msg, "reaction-player"); - const ign = await util.ignForUser(undefined, this.msg.author.id); - await new PlayerView(this.msg, ign).respond(); + await new PlayerView(this.msg, this.ign).respond(); }; return reactions; } async respond() { - await api.setUser(msg.author.id, ign); + console.log("************************"); + console.log(await this.text()); this.response = await util.respond(this.msg, await this.text(), this.response); if (!this.hasButtons) { await util.reactionButtons(this.response, - await this.buttons(player, matches)); + await this.buttons()); this.hasButtons = true; } return this.response; diff --git a/views/view.js b/views/view.js index 3d3ae38..771e3db 100644 --- a/views/view.js +++ b/views/view.js @@ -33,7 +33,15 @@ module.exports = class { // reply with embed + buttons this.response = await util.respond(this.msg, await this.embed(), this.response); - await util.reactionButtons(this.response, await this.buttons(), this.msg); + if (!this.hasButtons) { + await util.reactionButtons(this.response, await this.buttons(), this.msg); + this.hasButtons = true; + } + return this.response; + } + async error(text) { + // reply with error + this.response = await util.respond(this.msg, text, this.response); return this.response; } }; -- cgit v1.3.1 From 94adb2f9f182e50f6d60aadc70bb8bf8f74f2371 Mon Sep 17 00:00:00 2001 From: schneefux Date: Tue, 9 May 2017 13:42:45 +0200 Subject: more error handling --- commands/vainsocial/guild_create.js | 21 +++++++++++++-------- commands/vainsocial/guild_view.js | 8 +++++++- views/guild.js | 8 +------- views/guild_create.js | 5 +++-- 4 files changed, 24 insertions(+), 18 deletions(-) (limited to 'views') diff --git a/commands/vainsocial/guild_create.js b/commands/vainsocial/guild_create.js index ad8d0e7..c28fe2a 100644 --- a/commands/vainsocial/guild_create.js +++ b/commands/vainsocial/guild_create.js @@ -48,13 +48,18 @@ Create a Guild with your VainSocial profile as leader. // register a VainSocial Guild to a Discord account async run(msg, args) { util.trackAction(msg, "vainsocial-guild-create"); - // TODO error handling - await api.post("/guild", { - shard_id: args.region, - name: args.name, - identifier: args.tag, - user_token: msg.author.id - }); - await new GuildCreateView(msg, msg.author.id).respond(); + const guildCreateView = new GuildCreateView(msg, msg.author.id); + try { + await api.post("/guild", { + shard_id: args.region, + name: args.name, + identifier: args.tag, + user_token: msg.author.id + }); + } catch (err) { + console.error(err); + return await guildCreateView.error(err.error.err); + } + await guildCreateView.respond(); } }; diff --git a/commands/vainsocial/guild_view.js b/commands/vainsocial/guild_view.js index 7deb66f..472b284 100644 --- a/commands/vainsocial/guild_view.js +++ b/commands/vainsocial/guild_view.js @@ -33,6 +33,12 @@ Show a summary of your Guild. // show Guild details async run(msg, args) { util.trackAction(msg, "vainsocial-guild-view"); - await new GuildOverviewView(msg, msg.author.id).respond(); + const guildOverviewView = new GuildOverviewView(msg); + try { + const guild = await api.getGuild(msg.author.id); + await guildOverviewView.respond(guild); + } catch (err) { + return await guildOverviewView.error(err.error.err); + } } }; diff --git a/views/guild.js b/views/guild.js index 1a4cb78..a5eedef 100644 --- a/views/guild.js +++ b/views/guild.js @@ -12,11 +12,6 @@ const GuildOverviewView = module.exports; // match detail view module.exports = class extends View { - constructor(msg, user_token) { - super(msg); - this.user_token = user_token; - } - async text(members) { // TODO remove when API supports order by fame members = members.sort((m1, m2) => m1.fame < m2.fame); @@ -31,8 +26,7 @@ module.exports = class extends View { return embed; }; - async respond() { - const guild = await api.getGuild(this.user_token); + async respond(guild) { if (guild == undefined) { this.response = await util.respond(this.msg, strings.notRegistered, this.response); diff --git a/views/guild_create.js b/views/guild_create.js index 9720191..8e7be81 100644 --- a/views/guild_create.js +++ b/views/guild_create.js @@ -2,7 +2,8 @@ /* jshint esnext:true */ "use strict"; -const View = require("./view"), +const emoji = require("discord-emoji"), + View = require("./view"), util = require("../util"), api = require("../api"), strings = require("../strings"), @@ -29,7 +30,7 @@ module.exports = class extends View { let reactions = {}; reactions[emoji.symbols.information_source] = async () => { util.trackAction(this.msg, "reaction-guildview"); - await new GuildOverviewView(this.user_token).respond(); + await new GuildOverviewView(this.msg).respond(this.user_token); }; return reactions; } -- cgit v1.3.1 From 0ee8e0e5239e3f76911834da54d2c72a4a149804 Mon Sep 17 00:00:00 2001 From: schneefux Date: Tue, 9 May 2017 18:02:15 +0200 Subject: error handling, migration to template --- api.js | 2 +- commands/vainsocial/guild_add.js | 2 +- commands/vainsocial/guild_update.js | 33 +++++++++++++++++++++++++++------ commands/vainsocial/guild_view.js | 2 ++ commands/vainsocial/me.js | 3 ++- views/guild.js | 11 +++-------- views/guild_create.js | 2 +- views/guild_progress.js | 33 +++++++++++++++++++++++++++++++++ views/guild_update.js | 34 ++++++++++++++++++++++++++++++++++ views/matches.js | 7 +++---- views/player.js | 19 ++++++++----------- views/register.js | 6 ++---- 12 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 views/guild_progress.js create mode 100644 views/guild_update.js (limited to 'views') diff --git a/api.js b/api.js index 74614c2..c809309 100644 --- a/api.js +++ b/api.js @@ -200,7 +200,7 @@ module.exports.getGuild = async (token) => { // add user to guild module.exports.addToGuild = async (token, member) => { - const membership = await postFE("/guild/members", { + const membership = await api.postFE("/guild/members", { user_token: token, member_name: member }, "guild+" + token); diff --git a/commands/vainsocial/guild_add.js b/commands/vainsocial/guild_add.js index 8474724..8fe952c 100644 --- a/commands/vainsocial/guild_add.js +++ b/commands/vainsocial/guild_add.js @@ -33,7 +33,7 @@ Register IGNs to your Guild. await Promise.map(playersWaiters, async (waiter, idx) => { await api.upsearchPlayer(args[idx]); let success = false; - while (await waiter.next() != undefined) { + while (["stats_update", undefined].indexOf(await waiter.next())) { playersData[args[idx]] = await api.getPlayer(args[idx]); await guildAddView.respond(); success = true; diff --git a/commands/vainsocial/guild_update.js b/commands/vainsocial/guild_update.js index 66e8a5f..7d36b0e 100644 --- a/commands/vainsocial/guild_update.js +++ b/commands/vainsocial/guild_update.js @@ -26,21 +26,42 @@ Update the match history for all your Guild members. // internal / premium: immediately call backend player refresh async run(msg, args) { util.trackAction(msg, "vainsocial-guild-update"); + // obj of ign: player let playersData = {}; - const playersWaiters = args.map((name) => api.subscribeUpdates(name)), - guildUpdateView = new GuildMembersProgressView(msg, playersData); + // collect an array of IGNs + let names, guild; + const guildUpdateView = new GuildMembersProgressView(msg, + playersData); + try { + guild = await api.getGuild(msg.author.id); + names = guild.members.map((m) => m.player.name); + } catch (err) { + console.log(err); + return await guildUpdateView.error(err.error.err); + } + // update all the IGNs + const playersWaiters = names.map((name) => api.subscribeUpdates(name)); // create waiter dict & data dict await Promise.map(playersWaiters, async (waiter, idx) => { - await api.updatePlayer(args[idx]); + await api.updatePlayer(names[idx]); let success = false; - while (await waiter.next() != undefined) { - playersData[args[idx]] = await api.getPlayer(args[idx]); + while (["stats_update", undefined].indexOf(await waiter.next())) { + try { + playersData[names[idx]] = await api.getPlayer(names[idx]); + } catch (err) { + playersData[names[idx]] = undefined; + } await guildUpdateView.respond(); success = true; } }); await guildUpdateView.respond("Your Guild's fame is being updated…"); - await api.calculateGuild(guild.id, msg.author.id); + try { + await api.calculateGuild(guild.id, msg.author.id); + } catch (err) { + console.log(err); + await guildUpdateView.error(err.error.err); + } await guildUpdateView.respond("Your Guild was updated."); } }; diff --git a/commands/vainsocial/guild_view.js b/commands/vainsocial/guild_view.js index 472b284..c8375d4 100644 --- a/commands/vainsocial/guild_view.js +++ b/commands/vainsocial/guild_view.js @@ -5,6 +5,7 @@ const Commando = require("discord.js-commando"), oneLine = require("common-tags").oneLine, util = require("../../util"), + api = require("../../api"), GuildOverviewView = require("../../views/guild"); module.exports = class ViewGuildCommand extends Commando.Command { @@ -38,6 +39,7 @@ Show a summary of your Guild. const guild = await api.getGuild(msg.author.id); await guildOverviewView.respond(guild); } catch (err) { + console.log(err); return await guildOverviewView.error(err.error.err); } } diff --git a/commands/vainsocial/me.js b/commands/vainsocial/me.js index d2afb24..de87b4c 100644 --- a/commands/vainsocial/me.js +++ b/commands/vainsocial/me.js @@ -37,7 +37,8 @@ Store your in game name for quicker access to other commands and for Guild manag const registerView = new RegisterView(msg, args.name); await api.upsearchPlayer(args.name); try { - await api.subscribeUpdates(args.name).next(); + const waiter = api.subscribeUpdates(args.name); + while (await waiter.next() != "stats_update"); await api.setUser(msg.author.id, args.name); } catch (err) { console.log(err); diff --git a/views/guild.js b/views/guild.js index a5eedef..23c2afe 100644 --- a/views/guild.js +++ b/views/guild.js @@ -24,16 +24,11 @@ module.exports = class extends View { "", "vainsocial-guild-view") .setDescription(await this.text(guild.members)); return embed; - }; + } - async respond(guild) { - if (guild == undefined) { - this.response = await util.respond(this.msg, - strings.notRegistered, this.response); - return this.response; - } + async respond(guild, extra="") { this.response = await util.respond(this.msg, await this.embed(guild), this.response); return this.response; - }; + } } diff --git a/views/guild_create.js b/views/guild_create.js index 8e7be81..38720e1 100644 --- a/views/guild_create.js +++ b/views/guild_create.js @@ -38,7 +38,7 @@ module.exports = class extends View { // TODO move to super class async respond() { this.response = await util.respond(this.msg, - await this.text(), this.response); + await this.text() + "\n" + await this.help(), this.response); if (!this.hasButtons) { await util.reactionButtons(this.response, await this.buttons()); diff --git a/views/guild_progress.js b/views/guild_progress.js new file mode 100644 index 0000000..163c627 --- /dev/null +++ b/views/guild_progress.js @@ -0,0 +1,33 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildMembersProgressView = module.exports; + +module.exports = class extends View { + constructor(msg, players) { + super(msg); + this.players = players; + } + + // players: obj, key=ign, value=player + async text(players) { + return Object.entries(players).map((tuple) => + (tuple[1] == undefined)? + `Loading ${tuple[0]}…` + : `Loaded ${tuple[0]}.` + ).join("\n"); + } + + async respond(extra="") { + this.response = await util.respond(this.msg, + await this.text(this.players) + "\n" + extra, this.response); + return this.response; + }; +} diff --git a/views/guild_update.js b/views/guild_update.js new file mode 100644 index 0000000..cb32dc3 --- /dev/null +++ b/views/guild_update.js @@ -0,0 +1,34 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildAddView = module.exports; + +// match detail view +module.exports = class extends View { + constructor(msg, players) { + super(msg); + this.players = players; + } + + // players: obj, key=ign, value=player + async text(players) { + return Object.entries(players).map((tuple) => + (tuple[1] == undefined)? + `Loading ${tuple[0]}…` + : `Loaded ${tuple[0]}.` + ).join("\n"); + } + + async respond() { + this.response = await util.respond(this.msg, + await this.text(this.players), this.response); + return this.response; + }; +} diff --git a/views/matches.js b/views/matches.js index 279bf9c..a79654b 100644 --- a/views/matches.js +++ b/views/matches.js @@ -39,8 +39,8 @@ Score | ${emojiScore} \`${Math.floor(100 * participant.stats.impact_score)}%\` const matchesPart = matches.slice(0, MATCH_HISTORY_LEN); // build embed - let embed = util.vainsocialEmbed( - this.ign, "player/" + this.ign, "vainsocial-matches") + let embed = util.vainsocialEmbed(this.ign, + "player/" + this.ign, "vainsocial-matches") .setDescription(`Last ${matchesPart.length} casual and ranked matches.\n` + await this.help()) .setTimestamp(new Date(matchesPart[0].created_at)); @@ -60,9 +60,8 @@ Score | ${emojiScore} \`${Math.floor(100 * participant.stats.impact_score)}%\` let reactions = {}; matchesPart.forEach((m, idx) => reactions[strings.emojiCount[idx]] = async () => { - console.log("react"); util.trackAction(this.msg, "reaction-match", m.match_api_id); - await new MatchView(this.msg, m.match_api_id).respond(); + await new MatchView(this.msg).respond(m.match_api_id); }); return reactions; } diff --git a/views/player.js b/views/player.js index ec5d38b..a481c69 100644 --- a/views/player.js +++ b/views/player.js @@ -72,7 +72,7 @@ ${emoji.symbols["1234"]} or ${util.usg(this.msg, "vh " + this.ign)} for more*`; let reactions = {}; reactions[emoji.symbols.information_source] = async () => { util.trackAction(this.msg, "reaction-match", player.name); - await new MatchView(this.msg, matches[0].match_api_id).respond(); + await new MatchView(this.msg).respond(matches[0].match_api_id); }; reactions[emoji.symbols["1234"]] = async () => { util.trackAction(this.msg, "reaction-matches", player.name); @@ -82,16 +82,13 @@ ${emoji.symbols["1234"]} or ${util.usg(this.msg, "vh " + this.ign)} for more*`; } async respond() { - const [player, matches] = await Promise.all([ - api.getPlayer(this.ign), - api.getMatches(this.ign) - ]); - if (player == undefined) { - this.response = await util.respond(this.msg, - strings.loading(this.ign), this.response); - return this.response; - } - if (matches.length == 0) { + let player, matches; + try { + [player, matches] = await Promise.all([ + api.getPlayer(this.ign), + api.getMatches(this.ign) + ]); + } catch (err) { this.response = await util.respond(this.msg, strings.loading(this.ign), this.response); return this.response; diff --git a/views/register.js b/views/register.js index 9943299..1df3bf1 100644 --- a/views/register.js +++ b/views/register.js @@ -19,7 +19,7 @@ module.exports = class extends View { } async text() { - return `You are now registered at VainSocial, ${this.msg.author.mention}.`; + return `You are now registered at VainSocial, @${this.msg.author.tag}.`; } async help() { return `*${emoji.symbols.repeat} or ${util.usg(this.msg, "v")} to view your profile, ${util.usg(this.msg, "vgcreate")} to create a Guild*` @@ -33,10 +33,8 @@ module.exports = class extends View { return reactions; } async respond() { - console.log("************************"); - console.log(await this.text()); this.response = await util.respond(this.msg, - await this.text(), this.response); + await this.text() + "\n" + await this.help(), this.response); if (!this.hasButtons) { await util.reactionButtons(this.response, await this.buttons()); -- cgit v1.3.1 From d158b9dabfc8b6c9a6c13e3307be455957b95a98 Mon Sep 17 00:00:00 2001 From: schneefux Date: Tue, 9 May 2017 19:47:50 +0200 Subject: fix progress reports --- commands/vainsocial/guild_add.js | 28 +++++++++++++++++----------- commands/vainsocial/guild_rm.js | 14 ++++++++++---- commands/vainsocial/guild_update.js | 12 +++++------- views/guild_progress.js | 7 ++----- 4 files changed, 34 insertions(+), 27 deletions(-) (limited to 'views') diff --git a/commands/vainsocial/guild_add.js b/commands/vainsocial/guild_add.js index 75eb5f3..c1de856 100644 --- a/commands/vainsocial/guild_add.js +++ b/commands/vainsocial/guild_add.js @@ -26,25 +26,31 @@ Register IGNs to your Guild. } async run(msg, args) { util.trackAction(msg, "vainsocial-guild-add"); - let playersData = {}; + let playersStatus = {}; const playersWaiters = args.map((name) => api.subscribeUpdates(name)), - guildAddView = new GuildAddView(msg, playersData); + guildAddView = new GuildAddView(msg, playersStatus); // create waiter dict & data dict await Promise.each(playersWaiters, async (waiter, idx) => { await api.upsearchPlayer(args[idx]); let success = false; - while (["stats_update", "matches_update", undefined].indexOf( - await waiter.next())) { - try { - playersData[args[idx]] = await api.getPlayer(args[idx]); - success = true; - } catch (err) { - playersData[args[idx]] = undefined; - } - await guildAddView.respond(); + try { + do { + try { + await api.getPlayer(args[idx]); + playersStatus[args[idx]] = "Loading…"; + success = true; + } catch (err) { } + await guildAddView.respond(); + } while (["stats_update", "matches_update", undefined] + .indexOf(await waiter.next())); + } catch (err) { + console.error(err); + playersStatus[args[idx]] = err.error.err; + success = false; } if (success) { await api.addToGuild(msg.author.id, args[idx]); + playersStatus[args[idx]] = "Loaded."; } }); await guildAddView.respond("Your Guild members were added."); diff --git a/commands/vainsocial/guild_rm.js b/commands/vainsocial/guild_rm.js index 24bad36..4857c44 100644 --- a/commands/vainsocial/guild_rm.js +++ b/commands/vainsocial/guild_rm.js @@ -26,12 +26,18 @@ Remove an IGN from your Guild. } async run(msg, args) { util.trackAction(msg, "vainsocial-guild-rm"); - let playersData = {}; - const guildRmView = new GuildRmView(msg, playersData); + let playersStatus = {}; + const guildRmView = new GuildRmView(msg, playersStatus); try { await Promise.each(args, async (name) => { - await api.removeFromGuild(msg.author.id, name); - await guildRmView.respond(); + try { + playersStatus[name] = "Removing…"; + await api.removeFromGuild(msg.author.id, name); + playersStatus[name] = "Removed."; + await guildRmView.respond(); + } catch (err) { + playersStatus[name] = err.error.err; + } }); } catch (err) { console.error(err); diff --git a/commands/vainsocial/guild_update.js b/commands/vainsocial/guild_update.js index f0360ea..c3163be 100644 --- a/commands/vainsocial/guild_update.js +++ b/commands/vainsocial/guild_update.js @@ -27,11 +27,10 @@ Update the match history for all your Guild members. async run(msg, args) { util.trackAction(msg, "vainsocial-guild-update"); // obj of ign: player - let playersData = {}; + let playersStatus = {}; // collect an array of IGNs let names, guild; - const guildUpdateView = new GuildMembersProgressView(msg, - playersData); + const guildUpdateView = new GuildMembersProgressView(msg, playersStatus); try { guild = await api.getGuild(msg.author.id); names = guild.members.map((m) => m.player.name); @@ -44,15 +43,14 @@ Update the match history for all your Guild members. // create waiter dict & data dict await Promise.each(playersWaiters, async (waiter, idx) => { await api.updatePlayer(names[idx]); - let success = false; while (["stats_update", undefined].indexOf(await waiter.next())) { try { - playersData[names[idx]] = await api.getPlayer(names[idx]); + await api.getPlayer(names[idx]); + playersStatus[names[idx]] = "Loaded."; } catch (err) { - playersData[names[idx]] = undefined; + playersStatus[names[idx]] = "Loading…"; } await guildUpdateView.respond(); - success = true; } }); await guildUpdateView.respond("Your Guild's fame is being updated…"); diff --git a/views/guild_progress.js b/views/guild_progress.js index 163c627..b3fdeb3 100644 --- a/views/guild_progress.js +++ b/views/guild_progress.js @@ -16,13 +16,10 @@ module.exports = class extends View { this.players = players; } - // players: obj, key=ign, value=player + // players: obj, key=ign, value=progress async text(players) { return Object.entries(players).map((tuple) => - (tuple[1] == undefined)? - `Loading ${tuple[0]}…` - : `Loaded ${tuple[0]}.` - ).join("\n"); + `${tuple[0]}: ${tuple[1]}`).join("\n"); } async respond(extra="") { -- cgit v1.3.1 From 01a0f341cd19247c9b3d47bf62edefcb0c1545bc Mon Sep 17 00:00:00 2001 From: schneefux Date: Thu, 11 May 2017 20:08:25 +0200 Subject: guild view: sort by fame, show role; add guild role cmd --- api.js | 20 +++++++++++++++++ commands/vainsocial/guild_rm.js | 2 +- commands/vainsocial/guild_role.js | 47 +++++++++++++++++++++++++++++++++++++++ views/guild.js | 4 ++-- views/simple.js | 24 ++++++++++++++++++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 commands/vainsocial/guild_role.js create mode 100644 views/simple.js (limited to 'views') diff --git a/api.js b/api.js index eddac54..7f0c35d 100644 --- a/api.js +++ b/api.js @@ -71,6 +71,16 @@ module.exports.deleteFE = module.exports.delete = async (url, params={}, cacheke }); } +// send a PUT and optionally bust cache +module.exports.putFE = module.exports.put = async (url, params={}, cachekey=undefined) => { + if (cachekey) cache.del(cachekey); + return await request.put(API_FE_URL + url, { + form: params, + json: true, + forever: true + }); +} + module.exports.postBE = module.exports.backend = async (url) => { return await request.post({ uri: API_BE_URL + url, @@ -225,6 +235,16 @@ module.exports.removeFromGuild = async (token, member) => { return membership; } +// change a role +module.exports.changeRole = async (token, member, role) => { + const membership = await api.putFE("/guild/members/updateRole", { + user_token: token, + member_name: member, + new_role: role + }, "guild+" + token); + return membership; +} + // recalc fame, block until timeout or points update module.exports.calculateGuild = async (id, token) => { const channel = new Channel(), diff --git a/commands/vainsocial/guild_rm.js b/commands/vainsocial/guild_rm.js index 4857c44..e74aea4 100644 --- a/commands/vainsocial/guild_rm.js +++ b/commands/vainsocial/guild_rm.js @@ -13,7 +13,7 @@ module.exports = class AddGuildMemberCommand extends Commando.Command { constructor(client) { super(client, { name: "vainsocial-guildrm", - aliases: ["vguild-rm", "vgrm", "vgr"], + aliases: ["vguild-rm", "vgrm"], group: "vainsocial-guild", memberName: "vainsocial-guildrm", description: "Remove a member from your Guild.", diff --git a/commands/vainsocial/guild_role.js b/commands/vainsocial/guild_role.js new file mode 100644 index 0000000..b37bb79 --- /dev/null +++ b/commands/vainsocial/guild_role.js @@ -0,0 +1,47 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const Commando = require("discord.js-commando"), + oneLine = require("common-tags").oneLine, + Promise = require("bluebird"), + api = require("../../api"), + util = require("../../util"), + SimpleView = require("../../views/simple"); + +module.exports = class RoleGuildMemberCommand extends Commando.Command { + constructor(client) { + super(client, { + name: "vainsocial-guildrole", + aliases: ["vguild-role", "vgrole", "vgr"], + group: "vainsocial-guild", + memberName: "vainsocial-guildrole", + description: "Change a Guild member's role.", + examples: ["vgrole StormCallerSr Officer", "vgrole shutterfly Leader"], + args: [ { + key: "name", + label: "name", + prompt: "Please specify a Guild member's name.", + type: "string", + min: 2, + default: "?" + }, { + key: "role", + label: "role", + prompt: "Please specify the member's new role.", + type: "string" + } ] + }); + } + async run(msg, args) { + util.trackAction(msg, "vainsocial-guild-role"); + const simpleView = new SimpleView(msg); + try { + const member = await api.changeRole(msg.author.id, args.name, args.role); + await simpleView.respond(`Successfully changed ${args.name}'s role to ${args.role}.`); + } catch (err) { + console.log(err); + return await simpleView.error(err.error.err); + } + } +}; diff --git a/views/guild.js b/views/guild.js index 23c2afe..6a8658b 100644 --- a/views/guild.js +++ b/views/guild.js @@ -14,9 +14,9 @@ const GuildOverviewView = module.exports; module.exports = class extends View { async text(members) { // TODO remove when API supports order by fame - members = members.sort((m1, m2) => m1.fame < m2.fame); + members.sort((m1, m2) => m1.fame < m2.fame); return members.map((member) => - `${member.player.name} ${member.fame}`).join("\n"); + `${member.player.name} *${member.status}* ${member.fame}`).join("\n"); } async embed(guild) { diff --git a/views/simple.js b/views/simple.js new file mode 100644 index 0000000..5e8701a --- /dev/null +++ b/views/simple.js @@ -0,0 +1,24 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const SimpleView = module.exports; + +// just respond with a text +module.exports = class extends View { + async text(txt) { + return txt; + } + + async respond(text) { + this.response = await util.respond(this.msg, + await this.text(text), this.response); + return this.response; + } +} -- cgit v1.3.1 From f37c547a11b1102c8639219c532711b56a40103d Mon Sep 17 00:00:00 2001 From: schneefux Date: Thu, 11 May 2017 21:18:24 +0200 Subject: add vgmember command --- commands/vainsocial/guild_member.js | 47 +++++++++++++++++++++++++++++++++++++ views/guild.js | 7 +++--- views/guild_member.js | 35 +++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 commands/vainsocial/guild_member.js create mode 100644 views/guild_member.js (limited to 'views') diff --git a/commands/vainsocial/guild_member.js b/commands/vainsocial/guild_member.js new file mode 100644 index 0000000..2a1bf27 --- /dev/null +++ b/commands/vainsocial/guild_member.js @@ -0,0 +1,47 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const Commando = require("discord.js-commando"), + oneLine = require("common-tags").oneLine, + Promise = require("bluebird"), + api = require("../../api"), + util = require("../../util"), + GuildMemberView = require("../../views/guild_member"); + +module.exports = class ViewGuildMemberCommand extends Commando.Command { + constructor(client) { + super(client, { + name: "vainsocial-guildmember", + aliases: ["vguild-member", "vgm"], + group: "vainsocial-guild", + memberName: "vainsocial-guildmember", + description: "View a Guild member in detail.", + examples: ["vgm StormCallerSr"], + args: [ { + key: "name", + label: "name", + prompt: "Please specify a Guild member's name.", + type: "string", + min: 2, + default: "?" + } ] + }); + } + async run(msg, args) { + util.trackAction(msg, "vainsocial-guild-member"); + const guildMemberView = new GuildMemberView(msg); + try { + const guild = await api.getGuild(msg.author.id); + const member = guild.members.filter((m) => m.player.name == args.name)[0]; + if (member == undefined) + throw { err: { error: "Player is not in the Guild." } }; + const player = await api.getPlayer(member.player.name); + const matches = await api.getMatches(player.name); + await guildMemberView.respond(member, player, matches); + } catch (err) { + console.error(err); + return await guildMemberView.error(err.error.err); + } + } +}; diff --git a/views/guild.js b/views/guild.js index 6a8658b..91df6bb 100644 --- a/views/guild.js +++ b/views/guild.js @@ -3,20 +3,21 @@ "use strict"; const View = require("./view"), + GuildMemberView = require("./guild_member"), util = require("../util"), api = require("../api"), strings = require("../strings"), + Promise = require("bluebird"), oneLine = require("common-tags").oneLine; const GuildOverviewView = module.exports; // match detail view module.exports = class extends View { - async text(members) { + text(members) { // TODO remove when API supports order by fame members.sort((m1, m2) => m1.fame < m2.fame); - return members.map((member) => - `${member.player.name} *${member.status}* ${member.fame}`).join("\n"); + return members.map((m) => new GuildMemberView().text(m)).join("\n"); } async embed(guild) { diff --git a/views/guild_member.js b/views/guild_member.js new file mode 100644 index 0000000..d75ffe4 --- /dev/null +++ b/views/guild_member.js @@ -0,0 +1,35 @@ +#!/usr/bin/node +/* jshint esnext:true */ +"use strict"; + +const View = require("./view"), + PlayerView = require("./player"), + MatchesView = require("./matches"), + util = require("../util"), + api = require("../api"), + strings = require("../strings"), + oneLine = require("common-tags").oneLine; + +const GuildMemberView = module.exports; + +// combined fame + profile + last match +module.exports = class extends View { + text(member) { + return `${member.player.name} | ${member.status} | ${member.fame} VS Fame`; + } + + async embed(member, player, matches) { + const embed = util.vainsocialEmbed(`${member.player.name} - ${member.player.shard_id}`, + "", "vainsocial-guild-memberview") + .addField("Guild Profile", await this.text(member)) + .addField("Player Profile", await new PlayerView().text(player)) + .addField("Last Match", await new MatchesView().text(matches[0])); + return embed; + } + + async respond(member, player, matches) { + this.response = await util.respond(this.msg, + await this.embed(member, player, matches), this.response); + return this.response; + } +} -- cgit v1.3.1 From 4e4b66faf15c05416b807566218e8e6492fd7909 Mon Sep 17 00:00:00 2001 From: schneefux Date: Fri, 12 May 2017 16:57:41 +0200 Subject: vgme: fix mention not mentioning --- views/register.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'views') diff --git a/views/register.js b/views/register.js index 1df3bf1..b744bd1 100644 --- a/views/register.js +++ b/views/register.js @@ -19,7 +19,7 @@ module.exports = class extends View { } async text() { - return `You are now registered at VainSocial, @${this.msg.author.tag}.`; + return `You are now registered at VainSocial, ${this.msg.author.toString()}.`; } async help() { return `*${emoji.symbols.repeat} or ${util.usg(this.msg, "v")} to view your profile, ${util.usg(this.msg, "vgcreate")} to create a Guild*` -- cgit v1.3.1 From d6d157a67ea2fee48a41dada6eacb9ab6d3756f9 Mon Sep 17 00:00:00 2001 From: schneefux Date: Fri, 12 May 2017 17:03:40 +0200 Subject: guild view: order by fame (finally) --- views/guild.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'views') diff --git a/views/guild.js b/views/guild.js index 91df6bb..15863b0 100644 --- a/views/guild.js +++ b/views/guild.js @@ -15,9 +15,9 @@ const GuildOverviewView = module.exports; // match detail view module.exports = class extends View { text(members) { - // TODO remove when API supports order by fame - members.sort((m1, m2) => m1.fame < m2.fame); - return members.map((m) => new GuildMemberView().text(m)).join("\n"); + // TODO remove when API supports server side sort + return members.sort((m1, m2) => m1.fame < m2.fame).map((m) => + new GuildMemberView().text(m)).join("\n"); } async embed(guild) { -- cgit v1.3.1 From 54565aeccffb0722de554820eb856b23ac3c004f Mon Sep 17 00:00:00 2001 From: schneefux Date: Fri, 12 May 2017 20:13:50 +0200 Subject: fix update subscribe inf loop --- api.js | 1 + commands/vainsocial/me.js | 4 +--- views/register.js | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'views') diff --git a/api.js b/api.js index 0fe9e78..66d52da 100644 --- a/api.js +++ b/api.js @@ -170,6 +170,7 @@ module.exports.subscribeUpdates = (name, timeout=UPDATE_TIMEOUT) => { } }; } if (msg == Channel.DONE) { + subscribed = false; subscription.unsubscribe(); return undefined; } diff --git a/commands/vainsocial/me.js b/commands/vainsocial/me.js index de87b4c..8679a65 100644 --- a/commands/vainsocial/me.js +++ b/commands/vainsocial/me.js @@ -35,10 +35,8 @@ Store your in game name for quicker access to other commands and for Guild manag async run(msg, args) { util.trackAction(msg, "vainsocial-me", args.name); const registerView = new RegisterView(msg, args.name); - await api.upsearchPlayer(args.name); try { - const waiter = api.subscribeUpdates(args.name); - while (await waiter.next() != "stats_update"); + await api.upsearchPlayerSync(args.name); await api.setUser(msg.author.id, args.name); } catch (err) { console.log(err); diff --git a/views/register.js b/views/register.js index b744bd1..a636e84 100644 --- a/views/register.js +++ b/views/register.js @@ -4,6 +4,7 @@ const emoji = require("discord-emoji"), View = require("./view"), + PlayerView = require("./player"), util = require("../util"), api = require("../api"), strings = require("../strings"), -- cgit v1.3.1