summaryrefslogtreecommitdiff
path: root/views
diff options
context:
space:
mode:
Diffstat (limited to 'views')
-rw-r--r--views/guild.js35
-rw-r--r--views/guild_create.js49
-rw-r--r--views/guild_member.js35
-rw-r--r--views/guild_progress.js30
-rw-r--r--views/guild_update.js34
-rw-r--r--views/match.js53
-rw-r--r--views/matches.js80
-rw-r--r--views/player.js105
-rw-r--r--views/register.js46
-rw-r--r--views/simple.js24
-rw-r--r--views/view.js47
11 files changed, 538 insertions, 0 deletions
diff --git a/views/guild.js b/views/guild.js
new file mode 100644
index 0000000..15863b0
--- /dev/null
+++ b/views/guild.js
@@ -0,0 +1,35 @@
+#!/usr/bin/node
+/* jshint esnext:true */
+"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 {
+ text(members) {
+ // 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) {
+ const embed = util.vainsocialEmbed(`${guild.name} - ${guild.shard_id}`,
+ "", "vainsocial-guild-view")
+ .setDescription(await this.text(guild.members));
+ return embed;
+ }
+
+ 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
new file mode 100644
index 0000000..38720e1
--- /dev/null
+++ b/views/guild_create.js
@@ -0,0 +1,49 @@
+#!/usr/bin/node
+/* jshint esnext:true */
+"use strict";
+
+const emoji = require("discord-emoji"),
+ 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.msg).respond(this.user_token);
+ };
+ return reactions;
+ }
+
+ // TODO move to super class
+ async respond() {
+ this.response = await util.respond(this.msg,
+ await this.text() + "\n" + await this.help(), this.response);
+ if (!this.hasButtons) {
+ await util.reactionButtons(this.response,
+ await this.buttons());
+ this.hasButtons = true;
+ }
+ return this.response;
+ };
+}
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;
+ }
+}
diff --git a/views/guild_progress.js b/views/guild_progress.js
new file mode 100644
index 0000000..b3fdeb3
--- /dev/null
+++ b/views/guild_progress.js
@@ -0,0 +1,30 @@
+#!/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=progress
+ async text(players) {
+ return Object.entries(players).map((tuple) =>
+ `${tuple[0]}: ${tuple[1]}`).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/match.js b/views/match.js
new file mode 100644
index 0000000..2dc55ec
--- /dev/null
+++ b/views/match.js
@@ -0,0 +1,53 @@
+#!/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 {
+ // return [[title, text], …] for rosters
+ 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(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/matches.js b/views/matches.js
new file mode 100644
index 0000000..a79654b
--- /dev/null
+++ b/views/matches.js
@@ -0,0 +1,80 @@
+#!/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 () => {
+ util.trackAction(this.msg, "reaction-match", m.match_api_id);
+ await new MatchView(this.msg).respond(m.match_api_id);
+ });
+ 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..a481c69
--- /dev/null
+++ b/views/player.js
@@ -0,0 +1,105 @@
+#!/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).respond(matches[0].match_api_id);
+ };
+ 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() {
+ 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;
+ }
+ 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..a636e84
--- /dev/null
+++ b/views/register.js
@@ -0,0 +1,46 @@
+#!/usr/bin/node
+/* jshint esnext:true */
+"use strict";
+
+const emoji = require("discord-emoji"),
+ View = require("./view"),
+ PlayerView = require("./player"),
+ 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 {
+ constructor(msg, ign) {
+ super(msg);
+ this.ign = ign;
+ }
+
+ async text() {
+ 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*`
+ }
+ async buttons() {
+ let reactions = {};
+ reactions[emoji.symbols.repeat] = async () => {
+ util.trackAction(this.msg, "reaction-player");
+ await new PlayerView(this.msg, this.ign).respond();
+ };
+ return reactions;
+ }
+ async respond() {
+ this.response = await util.respond(this.msg,
+ await this.text() + "\n" + await this.help(), this.response);
+ if (!this.hasButtons) {
+ await util.reactionButtons(this.response,
+ await this.buttons());
+ this.hasButtons = true;
+ }
+ return this.response;
+ };
+}
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;
+ }
+}
diff --git a/views/view.js b/views/view.js
new file mode 100644
index 0000000..771e3db
--- /dev/null
+++ b/views/view.js
@@ -0,0 +1,47 @@
+#!/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);
+ 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;
+ }
+};