diff --git a/server/.env.example b/server/.env.example index 592d309c..c6be7f11 100644 --- a/server/.env.example +++ b/server/.env.example @@ -5,6 +5,10 @@ DISCORD_TOKEN= DISCORD_CLIENT_ID= DISCORD_CLIENT_SECRET= +TWITCH_CLIENT_ID= +TWITCH_CLIENT_SECRET= +TWITCH_REDIRECT_URI= + DISCORD_REDIRECT_URI=http://localhost:8080/auth/discord/return # NOTE: only use this as an override, use domains otherwise DISCORD_REDIRECT_DOMAINS=http://localhost:8080,https://dev.slmn.gg,https://slmn.gg diff --git a/server/package.json b/server/package.json index 7c29f78a..ae79ebfc 100644 --- a/server/package.json +++ b/server/package.json @@ -19,6 +19,8 @@ "nodemon": "^2.0.7" }, "dependencies": { + "@twurple/api": "^5.2.5", + "@twurple/auth": "^5.2.5", "airtable": "^0.10.1", "body-parser": "^1.19.0", "chalk": "^4.1.0", diff --git a/server/src/action-manager.js b/server/src/action-manager.js index 6da1a2d7..176faa47 100644 --- a/server/src/action-manager.js +++ b/server/src/action-manager.js @@ -56,6 +56,11 @@ async function load(expressApp, cors, Cache, io) { if (!authObjects.client) return res.status(403).send({ error: true, errorMessage: "No client data associated with this token" }); } + if (action.optionalParams) { + (action.optionalParams || []).forEach(key => { + params[key] = req.body[key] || null; + }); + } if (action.requiredParams) { (action.requiredParams || []).forEach(key => { params[key] = req.body[key]; @@ -65,16 +70,20 @@ async function load(expressApp, cors, Cache, io) { try { await action.handler( async (data) => res.send({ error: false, ...data }), - async (errorMessage, errorCode) => res.status(errorCode || 400).send({ - error: true, - errorMessage - }), + async (errorMessage, errorCode) => { + console.warn(`Error in action [${action.key}] ${errorCode} ${errorMessage}`); + res.status(errorCode || 400).send({ + error: true, + errorMessage + }); + }, params, authObjects, { updateRecord: (tableName, item, data) => updateRecord(Cache, tableName, item, data), get: Cache.get, createRecord: (tableName, data) => createRecord(Cache, tableName, data), + auth: Cache.auth } ); } catch (e) { @@ -129,6 +138,7 @@ async function load(expressApp, cors, Cache, io) { updateRecord: (tableName, item, data) => updateRecord(Cache, tableName, item, data), get: Cache.get, createRecord: (tableName, data) => createRecord(Cache, tableName, data), + auth: Cache.auth } ); } catch (e) { diff --git a/server/src/action-utils.js b/server/src/action-utils.js index 2878fbbb..bac03da6 100644 --- a/server/src/action-utils.js +++ b/server/src/action-utils.js @@ -54,7 +54,7 @@ async function updateRecord(Cache, tableName, item, data) { await slmngg(tableName).update(item.id, data); } catch (e) { console.error("Airtable update failed", e); - return { error: true}; + return { error: true }; } } @@ -70,14 +70,15 @@ async function createRecord(Cache, tableName, records) { // TODO: think about how eager update would work try { - let newRecords = await slmngg(tableName).create(records); + let newRecords = await slmngg(tableName).create(records.map(recordData => ({ fields: recordData }))); newRecords.forEach(record => { Cache.set(cleanID(record.id), deAirtable(record.fields), { eager: true }); }); console.log(newRecords.length); console.log(newRecords); } catch (e) { - return { error: true }; + console.error("Airtable create failed", e); + return { error: true, errorMessage: e.message }; } } diff --git a/server/src/actions/manage-prediction.js b/server/src/actions/manage-prediction.js new file mode 100644 index 00000000..b2566077 --- /dev/null +++ b/server/src/actions/manage-prediction.js @@ -0,0 +1,154 @@ +const { ApiClient } = require("@twurple/api"); +const { StaticAuthProvider, refreshUserToken } = require("@twurple/auth"); + +const automaticPredictionTitleStartCharacter = "⬥"; + +function generatePredictionTitle(map) { + let title; + if (map.number) { + if (map.name) { + title = `Who will win map ${map.number} - ${map.name}?`; + } else { + title = `Who will win map ${map.number}?`; + } + + } else if (map.name) { + title = `Who will win ${map.name}?`; + } else { + title = "Who will win the map?"; + } + return `${automaticPredictionTitleStartCharacter} ${title}`; +} + +function getTargetPrediction(predictions, teams) { + return predictions.find(p => + ["ACTIVE", "LOCKED"].includes(p.status) && + p.outcomes.every(outcome => [...teams.map(t => t.name), "Draw"].includes(outcome.title)) && + p.title.startsWith(automaticPredictionTitleStartCharacter) + ); +} + +module.exports = { + key: "manage-prediction", + auth: ["client"], + requiredParams: ["predictionAction"], + optionalParams: ["autoLockAfter"], + /*** + * @param {ActionSuccessCallback} success + * @param {ActionErrorCallback} error + * @param {PredictionAction} predictionAction + * @param {number?} autoLockAfter + * @param {ClientData} client + * @param {CacheGetFunction} get + * @param {CacheAuthFunctions} auth + * @param {SimpleUpdateRecord} updateRecord + * @returns {Promise} + */ + // eslint-disable-next-line no-empty-pattern + async handler(success, error, { predictionAction, autoLockAfter = 120 }, { client }, { get, auth }) { + if (!(["create", "lock", "resolve", "cancel"].includes(predictionAction))) return error("Invalid action"); + console.log(predictionAction); + + const broadcast = await get(client?.broadcast?.[0]); + if (!broadcast) return error("No broadcast associated"); + if (!broadcast.channel) return error("No channel associated with broadcast"); + + const channel = await auth.getChannel(broadcast?.channel?.[0]); + if (!channel.twitch_refresh_token) return error("No twitch auth token associated with channel"); + if (!channel.channel_id || !channel.name || !channel.twitch_scopes) return error("Invalid channel data"); + let scopes = channel.twitch_scopes.split(" "); + if (!["channel:manage:predictions", "channel:read:predictions"].every(scope => scopes.includes(scope))) return error("Token doesn't have the required scopes"); + + console.log(channel); + const accessToken = await auth.getTwitchAccessToken(channel); + + const authProvider = new StaticAuthProvider(process.env.TWITCH_CLIENT_ID, accessToken); + const api = new ApiClient({authProvider}); + + // TODO: move cancel action to here + + const match = await get(broadcast?.live_match?.[0]); + if (!match) return error("No match associated"); + + const team1 = await get(match?.teams?.[0]); + const team2 = await get(match?.teams?.[1]); + if (!team1 || !team2) return error("Did not find two teams!"); + + const maps = await Promise.all((match.maps || []).map(async m => { + let map = await get(m); + + if (map?.map?.[0]) { + let mapData = await get(map?.map?.[0]); + map.map = mapData; + } + + if (map?.winner?.[0]) { + let winner = await get(map?.winner?.[0]); + map.winner = winner; + } + + return map; + })); + if (maps.length === 0) return error("No maps associated with match"); + + const { data: predictions } = await api.predictions.getPredictions(channel.channel_id); + + + if (["create", "lock"].includes(predictionAction)) { + const currentMap = maps.filter(m => !m.dummy && !m.winner && !m.draw && !m.banner)[0]; + if (!currentMap) return error("No valid map to start a prediction for"); + + + const targetPrediction = getTargetPrediction(predictions, [team1, team2]); + console.log(targetPrediction); + + if (predictionAction === "create") { + if (targetPrediction) return error("Prediction already exists"); + const predictionTitle = generatePredictionTitle(currentMap); + + let outcomes = [team1.name, team2.name]; + + if (!(currentMap && currentMap.map.type === "Control")) { + outcomes.push("Draw"); + } + + const responsePrediction = await api.predictions.createPrediction(channel.channel_id, { + title: predictionTitle, + outcomes: outcomes, + autoLockAfter: autoLockAfter || 120 + }); + console.log(responsePrediction); + return success(); // TODO: check responsePrediction for errors + } + + if (!targetPrediction) return error("Prediction does not exist"); + + if (predictionAction === "lock") { + const responsePrediction = await api.predictions.lockPrediction(channel.channel_id, targetPrediction.id); + console.log(responsePrediction); + } + + } else if (["resolve"].includes(predictionAction)) { + const lastMap = maps.filter(m => !m.dummy && !m.banner && (m.winner || m.draw)).pop(); + const targetPrediction = getTargetPrediction(predictions, [team1, team2]); + console.log(targetPrediction); + + if (lastMap.draw) { + const responsePrediction = await api.predictions.resolvePrediction(channel.channel_id, targetPrediction.id, targetPrediction.outcomes.find(o => o.title === "Draw").id); + console.log(responsePrediction); + } else { + const responsePrediction = await api.predictions.resolvePrediction(channel.channel_id, targetPrediction.id, targetPrediction.outcomes.find(o => o.title === lastMap.winner.name).id); + console.log(responsePrediction); + } + + } else if (["cancel"].includes(predictionAction)) { + const activePredictions = predictions.filter(p => ["ACTIVE", "LOCKED"].includes(p.status)); + for (const prediction of activePredictions) { + const responsePrediction = await api.predictions.cancelPrediction(channel.channel_id, prediction.id); + console.log(responsePrediction); + } + } + + return success(); + } +}; diff --git a/server/src/actions/set-title.js b/server/src/actions/set-title.js new file mode 100644 index 00000000..200d59ee --- /dev/null +++ b/server/src/actions/set-title.js @@ -0,0 +1,89 @@ +const { ApiClient } = require("@twurple/api"); +const { StaticAuthProvider } = require("@twurple/auth"); +module.exports = { + key: "set-title", + auth: ["client"], + /*** + * @param {ActionSuccessCallback} success + * @param {ActionErrorCallback} error + * @param {PredictionAction} predictionAction + * @param {number?} autoLockAfter + * @param {ClientData} client + * @param {CacheGetFunction} get + * @param {CacheAuthFunctions} auth + * @param {SimpleUpdateRecord} updateRecord + * @returns {Promise} + */ + // eslint-disable-next-line no-empty-pattern + async handler(success, error, { predictionAction, autoLockAfter = 120 }, { client }, { get, auth }) { + + const broadcast = await get(client?.broadcast?.[0]); + if (!broadcast) return error("No broadcast associated"); + if (!broadcast.channel) return error("No channel associated with broadcast"); + + const event = await get(broadcast.event?.[0]); + if (!event) return error("No event associated with broadcast"); + + + const channel = await auth.getChannel(broadcast?.channel?.[0]); + if (!channel.twitch_refresh_token) return error("No twitch auth token associated with channel"); + if (!channel.channel_id || !channel.name || !channel.twitch_scopes) return error("Invalid channel data"); + let scopes = channel.twitch_scopes.split(" "); + if (!["channel:manage:broadcast"].every(scope => scopes.includes(scope))) return error("Token doesn't have the required scopes"); + + const accessToken = await auth.getTwitchAccessToken(channel); + + const authProvider = new StaticAuthProvider(process.env.TWITCH_CLIENT_ID, accessToken); + const api = new ApiClient({authProvider}); + + + const match = await get(broadcast?.live_match?.[0]); + if (!match) return error("No match associated"); + + const team1 = await get(match?.teams?.[0]); + const team2 = await get(match?.teams?.[1]); + if (!team1 || !team2) return error("Did not find two teams!"); + + const formatOptions = { + "event": event.name, + "event_long": event.name, + "event_short": event.short, + "team_1_code": team1.code, + "team_1_name": team1.name, + "team_2_code": team2.code, + "team_2_name": team2.name, + "match_sub_event": match.sub_event, + "match_round": match.round, + "match_number": match.match_number, + }; + + let newTitle = broadcast.title_format; + + Object.entries(formatOptions).forEach(([key, val]) => { + newTitle = newTitle.replace(`{${key}}`, val); + }); + + const gameMap = { + "Overwatch": "Overwatch 2", + "Valorant": "VALORANT", + "League of Legends": "League of Legends" + }; + + if (event.game && gameMap[event.game]) { + const game = await api.games.getGameByName(gameMap[event.game]); + const channelInfo = api.channels.updateChannelInfo(channel.channel_id, { + title: newTitle, + gameId: game.id + }); + console.log(channelInfo); + } else { + const channelInfo = api.channels.updateChannelInfo(channel.channel_id, { + title: newTitle + }); + console.log(channelInfo); + } + + return success(); + // return response?.error ? error("Airtable error", 500) : success(); + } +}; diff --git a/server/src/actions/update-map-data.js b/server/src/actions/update-map-data.js index 15c8dd04..d4eece41 100644 --- a/server/src/actions/update-map-data.js +++ b/server/src/actions/update-map-data.js @@ -114,7 +114,7 @@ module.exports = { if (map.score_1) fieldData["Score 1"] = map.score_1; if (map.score_2) fieldData["Score 2"] = map.score_2; - return { fields: fieldData }; + return fieldData; }); if (recordCreations.length) { diff --git a/server/src/airtable-interface.js b/server/src/airtable-interface.js index 42823881..23bcec8f 100644 --- a/server/src/airtable-interface.js +++ b/server/src/airtable-interface.js @@ -82,7 +82,7 @@ function setRebuilding(isRebuilding) { // Starting with syncing Matches // const tables = ["Matches", "Teams", "Themes", "Events", "Players", "Player Relationships"]; -const tables = ["Broadcasts", "Clients", "Players", "Events", "Event Series", "Teams", "Ad Reads", "Ad Read Groups", "News", "Matches", "Themes", "Socials", "Accolades", "Player Relationships", "Brackets", "Live Guests", "Headlines", "Maps", "Map Data", "Heroes", "Log Files", "Tracks", "Track Groups", "Track Group Roles"]; +const tables = ["Broadcasts", "Clients", "Players", "Channels", "Events", "Event Series", "Teams", "Ad Reads", "Ad Read Groups", "News", "Matches", "Themes", "Socials", "Accolades", "Player Relationships", "Brackets", "Live Guests", "Headlines", "Maps", "Map Data", "Heroes", "Log Files", "Tracks", "Track Groups", "Track Group Roles"]; const staticTables = ["Redirects"]; function deAirtable(obj) { diff --git a/server/src/cache.js b/server/src/cache.js index cf358e03..0f366fd7 100644 --- a/server/src/cache.js +++ b/server/src/cache.js @@ -1,5 +1,7 @@ const crypto = require("crypto"); - +const { accessTokenIsExpired, + refreshUserToken +} = require("@twurple/auth"); /* - Get and set data - Store data @@ -190,6 +192,11 @@ async function set(id, data, options) { // }); } + if (data?.__tableName === "Channels") { + auth.set(`channel_${id}`, data); + return; // not setting it on global requestble store + } + if (data?.__tableName === "Events") { // update antileak if (!data.antileak?.length) { @@ -304,12 +311,33 @@ async function getPlayer(discordID) { return players.get(discordID); } +async function getChannel(airtableID) { + return auth.get(`channel_${cleanID(airtableID)}`); +} + +async function getTwitchAccessToken(channel) { + // get stored access token, check if it's valid + // otherwise / or if no token, get from refresh token + let storedToken = auth.get(`twitch_access_token_${channel.channel_id}`); + + if (!storedToken || accessTokenIsExpired(storedToken)) { + // refresh token + let token = await refreshUserToken(process.env.TWITCH_CLIENT_ID, process.env.TWITCH_CLIENT_SECRET, channel.twitch_refresh_token); + auth.set(`twitch_access_token_${channel.channel_id}`, token); + return token; + + } + return storedToken; +} + module.exports = { set, get, setup, onUpdate, auth: { start: authStart, getData: getAuthenticatedData, - getPlayer + getPlayer, + getChannel, + getTwitchAccessToken } }; diff --git a/server/src/routes.js b/server/src/routes.js index db709681..710f17a4 100644 --- a/server/src/routes.js +++ b/server/src/routes.js @@ -1,3 +1,9 @@ +const fetch = require("node-fetch"); +const { updateRecord, + createRecord +} = require("./action-utils"); +const { exchangeCode } = require("@twurple/auth"); + function cleanID(id) { if (!id) return null; if (typeof id !== "string") return id.id || null; // no real id oops @@ -282,4 +288,70 @@ module.exports = ({ app, cors, Cache, io }) => { return res.status(500).send(e.message); } }); + + let states = {}; + + function createState() { + // return a uuid without a library + let uuid = ""; + for (let i = 0; i < 32; i++) { + uuid += Math.floor(Math.random() * 16).toString(16); + } + return uuid; + } + const TwitchEnvSet = ["TWITCH_REDIRECT_URI", "TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"].every(key => !!process.env[key]); + if (!TwitchEnvSet) { + console.error("Twitch authentication on the server is disabled. Set TWITCH_ keys in server/.env to enable it."); + } + + app.get("/twitch_auth/:scopes", (req, res) => { + if (!TwitchEnvSet) return res.status(503).send({ error: true, message: "Twitch authentication is disabled on the server." }); + let state = createState(); + states[state] = req.params.scopes; + res.redirect(`https://id.twitch.tv/oauth2/authorize?client_id=${process.env.TWITCH_CLIENT_ID}&redirect_uri=${process.env.TWITCH_REDIRECT_URI}&response_type=code&scope=${req.params.scopes}&force_verify=true`); + }); + + + app.get("/twitch_callback", async(req, res) => { + if (!TwitchEnvSet) return res.status(503).send({ error: true, message: "Twitch authentication is disabled on the server." }); + try { + const tokenInfo = await exchangeCode(process.env.TWITCH_CLIENT_ID, process.env.TWITCH_CLIENT_SECRET, req.query.code, process.env.TWITCH_REDIRECT_URI); + + let scopes = states[req.query.state]; + if (scopes) delete states[req.query.state]; + + // get or create channel in table + const channelIDs = (await Cache.get("Channels"))?.ids || []; + const channels = await Promise.all(channelIDs.map(id => Cache.get(id))); + console.log(channels); + const existingChannel = channels.find(c => c.channel_id === tokenInfo.userId); + + let airtableResponse; + // store into channels table with tokens + scopes + + if (existingChannel) { + airtableResponse = await updateRecord(Cache, "Channels", existingChannel.id, { + "Twitch Refresh Token": tokenInfo.accessToken, + "Twitch Scopes": tokenInfo.scopes.join(" "), + "Channel ID": tokenInfo.userId, + "Name": tokenInfo.userName + }); + } else { + airtableResponse = await createRecord(Cache, "Channels", [{ + "Twitch Refresh Token": tokenInfo.accessToken, + "Twitch Scopes": tokenInfo.scopes.join(" "), + "Channel ID": tokenInfo.userId, + "Name": tokenInfo.userName + }]); + } + + // console.log(airtableResponse); + + return res.send("okay thanks"); + } catch (e) { + console.error("[Twitch Auth] error", e); + res.status(400).send({ error: true, errorMessage: e.message}); + } + }); + }; diff --git a/server/src/types.js b/server/src/types.js index 21d39b72..59c166e4 100644 --- a/server/src/types.js +++ b/server/src/types.js @@ -27,6 +27,22 @@ * @property {DirtyAirtableID} id */ +/** + * @typedef {Object} CacheAuthFunctions + * @property {CacheGetAuthFunction} getChannel - Get Twitch channel data from Airtable + * @property {function} getTwitchAccessToken - Get refreshed access token from Twitch + */ + +/** + * @typedef CacheGetAuthFunction + * @param {AnyAirtableID} airtableID - ID from Channels table + */ +/** + * @typedef TwitchGetOrRefreshToken + * @param {object} channel - Channels table object + * @param {string} object.channel_id - Twitch channel ID + * @param {string} object.twitch_refresh_token - Twitch refresh token + */ /** * @typedef {Object} DiscordUserData @@ -105,3 +121,7 @@ * @param {object} changes - New data to change * @returns {Promise} */ + +/** + * @typedef {'create'|'lock'|'resolve'|'cancel'} PredictionAction + */ diff --git a/server/yarn.lock b/server/yarn.lock index 4f4db2ce..1e0234b4 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -23,6 +23,72 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@d-fischer/cache-decorators@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@d-fischer/cache-decorators/-/cache-decorators-3.0.0.tgz#6456c3f8c74c4dd22d80faa7985d4efff6d2ff77" + integrity sha512-mYUCjrp5hJgimceC5bof3zzmElyxzW4ty+73IjY12wvxLAqsq0CbgLGspnJm6KgwEfGoeRnISZD4EXJidG3FvA== + dependencies: + "@d-fischer/shared-utils" "^3.0.1" + "@types/node" "^14.14.22" + tslib "^2.1.0" + +"@d-fischer/cross-fetch@^4.0.2": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@d-fischer/cross-fetch/-/cross-fetch-4.1.0.tgz#6a25d3244c5014ccd97a7f1056f7f7b619aca6a3" + integrity sha512-HH87JacceXOANr5XuBuSIQmPRPOvOUPwU1JR0DLUT6A8jGcP0jq2YUAiFCyZ8VGwDbTjsD3CZFpWIp5o7mIFWQ== + dependencies: + node-fetch "2.6.7" + +"@d-fischer/detect-node@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@d-fischer/detect-node/-/detect-node-3.0.1.tgz#7b051a86611b0396ba205aabae805b18cc642a78" + integrity sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w== + +"@d-fischer/logger@^4.0.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@d-fischer/logger/-/logger-4.2.0.tgz#6ba05043c3a49cef3ebf5bc6f7825566dca37d3c" + integrity sha512-Hgm/GfeZfv2UPcRY4uF4S7OtCCv6Xxx++q7FAXc1qMrInaowKrmpo6YdFwRfZ8z53gzXFhc6H8j1Ttm0046uTw== + dependencies: + "@d-fischer/shared-utils" "^3.2.0" + detect-node "^2.0.4" + tslib "^2.0.3" + +"@d-fischer/promise.allsettled@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@d-fischer/promise.allsettled/-/promise.allsettled-2.0.2.tgz#2f8d55dba8412f656d62885e723e9b591d45f71c" + integrity sha512-xY0vYDwJYFe22MS5ccQ50N4Mcc2nQ8J4eWE5Y354IxZwW32O5uTT6mmhFSuVF6ZrKvzHOCIrK+9WqOR6TI3tcA== + dependencies: + array.prototype.map "^1.0.3" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + get-intrinsic "^1.0.2" + iterate-value "^1.0.2" + +"@d-fischer/qs@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@d-fischer/qs/-/qs-7.0.2.tgz#21942f51590e20954086bdc32fb3e608d3525659" + integrity sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ== + +"@d-fischer/rate-limiter@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@d-fischer/rate-limiter/-/rate-limiter-0.6.1.tgz#5134640e32831e22d190fc8a08b89dba38af3e87" + integrity sha512-SqsMxs77c/W9knmcX9ccy12ASq+E5UXzxU/ZIDAhZyll+2IqyikhgLQUu5n7bdxb3kox+TOSCIEFxDSSaGhcTA== + dependencies: + "@d-fischer/logger" "^4.0.0" + "@d-fischer/promise.allsettled" "^2.0.2" + "@d-fischer/shared-utils" "^3.2.0" + "@types/node" "^12.12.5" + tslib "^2.0.3" + +"@d-fischer/shared-utils@^3.0.1", "@d-fischer/shared-utils@^3.2.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@d-fischer/shared-utils/-/shared-utils-3.3.0.tgz#07e4b1c3251fdb7232293d095939176a5529ecd9" + integrity sha512-k9kOsggH+xyFf4+p63TI1kbG1oQX2qHra3OY7LGg2+26JMgL0RD6Hss86QXB4bx0SFielmdA9OD35NETZ/E51g== + dependencies: + "@types/node" "^14.11.2" + tslib "^2.0.3" + "@discordjs/builders@^0.16.0": version "0.16.0" resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.16.0.tgz#3201f57fa57c4dd77aebb480cf47da77b7ba2e8c" @@ -79,6 +145,51 @@ dependencies: defer-to-connect "^1.0.1" +"@twurple/api-call@^5.2.5": + version "5.2.5" + resolved "https://registry.yarnpkg.com/@twurple/api-call/-/api-call-5.2.5.tgz#415b291602431f444ffaee588a4d9af772c58e52" + integrity sha512-oPbkNnh6zT6twNFmt2FOJwK+KQjlJvWKZUcvKW/P/JGe9olIBfNIScVcmnAEr9olOvBoipnF/xLu0V0f0C/UeQ== + dependencies: + "@d-fischer/cross-fetch" "^4.0.2" + "@d-fischer/qs" "^7.0.2" + "@twurple/common" "^5.2.5" + "@types/node-fetch" "^2.5.7" + tslib "^2.0.3" + +"@twurple/api@^5.2.5": + version "5.2.5" + resolved "https://registry.yarnpkg.com/@twurple/api/-/api-5.2.5.tgz#1a820c2d509400ab06c27e17b639fc23f68f5009" + integrity sha512-EzIJToZOimXEaZt6uGZTj+6iyD+L2uUiHeSjfMdY+KwIbdxM99218YFSOA8l2RHRSBrNkt2rYEau45oFOP/RcA== + dependencies: + "@d-fischer/cache-decorators" "^3.0.0" + "@d-fischer/detect-node" "^3.0.1" + "@d-fischer/logger" "^4.0.0" + "@d-fischer/rate-limiter" "^0.6.1" + "@d-fischer/shared-utils" "^3.2.0" + "@twurple/api-call" "^5.2.5" + "@twurple/common" "^5.2.5" + tslib "^2.0.3" + +"@twurple/auth@^5.2.5": + version "5.2.5" + resolved "https://registry.yarnpkg.com/@twurple/auth/-/auth-5.2.5.tgz#3091111194a5ceebc1fa4ce6cd9d8f3219a4ab20" + integrity sha512-gu2D3FU87MtXhSnxmiHgTJrOig57ttx89YvamqBq9hT4QpId23Z5H0wUtvumL+G3NlyhcJ6+LOYvWiz8MFPRWA== + dependencies: + "@d-fischer/logger" "^4.0.0" + "@d-fischer/shared-utils" "^3.2.0" + "@twurple/api-call" "^5.2.5" + "@twurple/common" "^5.2.5" + tslib "^2.0.3" + +"@twurple/common@^5.2.5": + version "5.2.5" + resolved "https://registry.yarnpkg.com/@twurple/common/-/common-5.2.5.tgz#ceb97aa4df5e5e91e6ef3dedc52c3cf7c8b7ea54" + integrity sha512-pZe/IjdNfxiKtVw+3ntViq0gMnZrx8x+VB3aAzl/hB6bE10iUB0vV0qK7gCrvDJCnMDk0oGFLdl+nQOwkDHVpQ== + dependencies: + "@d-fischer/shared-utils" "^3.2.0" + klona "^2.0.4" + tslib "^2.0.3" + "@types/component-emitter@^1.2.10": version "1.2.10" resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" @@ -94,7 +205,7 @@ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== -"@types/node-fetch@^2.6.2": +"@types/node-fetch@^2.5.7", "@types/node-fetch@^2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== @@ -112,6 +223,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== +"@types/node@^12.12.5": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/node@^14.11.2", "@types/node@^14.14.22": + version "14.18.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.31.tgz#4b873dea3122e71af4f77e65ec5841397ff254d3" + integrity sha512-vQAnaReSQkEDa8uwAyQby8bYGKu84R/deEc6mg5T8fX6gzCn8QW6rziSgsti1fNvsrswKUKPnVTi7uoB+u62Mw== + "@types/ws@^8.5.3": version "8.5.3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" @@ -264,6 +385,17 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array.prototype.map@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.4.tgz#0d97b640cfdd036c1b41cfe706a5e699aa0711f2" + integrity sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -379,7 +511,7 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -call-bind@^1.0.0: +call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== @@ -666,6 +798,14 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -691,6 +831,11 @@ detect-libc@^2.0.0: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + discord-api-types@^0.33.5: version "0.33.5" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.33.5.tgz#6548b70520f7b944c60984dca4ab58654d664a12" @@ -794,6 +939,64 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" +es-abstract@^1.18.0-next.2, es-abstract@^1.19.0, es-abstract@^1.19.5: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.0" + has-symbols "^1.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.5" + isarray "^2.0.5" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" @@ -1074,11 +1277,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -1102,6 +1320,15 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1116,6 +1343,14 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -1183,6 +1418,11 @@ graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1193,11 +1433,30 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + has-symbols@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -1310,16 +1569,40 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arguments@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1334,6 +1617,11 @@ is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.0" +is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -1341,6 +1629,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1383,6 +1678,16 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" @@ -1408,11 +1713,45 @@ is-path-inside@^3.0.1: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -1423,11 +1762,23 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -1438,6 +1789,19 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iterate-iterator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" + integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== + +iterate-value@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1478,6 +1842,11 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +klona@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + latest-version@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -1673,18 +2042,18 @@ node-addon-api@^4.3.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + nodemon@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.7.tgz#6f030a0a0ebe3ea1ba2a38f71bf9bab4841ced32" @@ -1738,6 +2107,26 @@ object-assign@^4, object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -1964,6 +2353,15 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" @@ -2025,6 +2423,15 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -2119,6 +2526,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.0: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -2234,6 +2650,24 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -2380,7 +2814,7 @@ ts-mixer@^6.0.1: resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a" integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg== -tslib@^2.4.0: +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -2424,6 +2858,16 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + undefsafe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" @@ -2516,6 +2960,17 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" diff --git a/website/src/components/website/dashboard/Predictions.vue b/website/src/components/website/dashboard/Predictions.vue new file mode 100644 index 00000000..961967cc --- /dev/null +++ b/website/src/components/website/dashboard/Predictions.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/website/src/utils/dashboard.js b/website/src/utils/dashboard.js index d3bb4556..1e061bfa 100644 --- a/website/src/utils/dashboard.js +++ b/website/src/utils/dashboard.js @@ -36,6 +36,22 @@ export async function updateMatchData(auth, match, updatedData) { }); } +export async function managePred(auth, client, predictionAction) { + if (!auth?.user) return { error: true, errorMessage: "Not authenticated" }; + return await authenticatedRequest(auth, "actions/manage-prediction", { + client: client.id || client, + predictionAction//, + // autoLockAfter: 60 + }); +} + +export async function updateAutomaticTitle(auth, client) { + if (!auth?.user) return { error: true, errorMessage: "Not authenticated" }; + return await authenticatedRequest(auth, "actions/set-title", { + client: client.id || client + }); +} + export async function updateMapData(auth, match, mapData) { if (!auth?.user) return { error: true, errorMessage: "Not authenticated" }; return await authenticatedRequest(auth, "actions/update-map-data", { diff --git a/website/src/views/Dashboard.vue b/website/src/views/Dashboard.vue index a7f2b9d9..4495133c 100644 --- a/website/src/views/Dashboard.vue +++ b/website/src/views/Dashboard.vue @@ -12,6 +12,10 @@
+ + + Update title + @@ -21,12 +25,13 @@ import { url } from "@/utils/content-utils"; import BroadcastSwitcher from "@/components/website/dashboard/BroadcastSwitcher"; import MatchThumbnail from "@/components/website/match/MatchThumbnail"; import MatchEditor from "@/components/website/dashboard/MatchEditor"; -import { BFormCheckbox } from "bootstrap-vue"; -import { togglePlayerCams } from "@/utils/dashboard"; +import { BButton, BFormCheckbox } from "bootstrap-vue"; +import { togglePlayerCams, updateAutomaticTitle } from "@/utils/dashboard"; +import Predictions from "@/components/website/dashboard/Predictions"; export default { name: "Dashboard", - components: { MatchEditor, MatchThumbnail, BroadcastSwitcher, BFormCheckbox }, + components: { Predictions, MatchEditor, MatchThumbnail, BroadcastSwitcher, BFormCheckbox, BButton }, computed: { user() { if (!this.$root.auth.user?.airtableID) return {}; @@ -67,7 +72,12 @@ export default { } }, methods: { - url, togglePlayerCams + url, + togglePlayerCams, + + async updateTitle() { + await updateAutomaticTitle(this.$root.auth, "self", "create"); + } } };