diff --git a/app/package.json b/app/package.json index 15c670f8..c66846e0 100644 --- a/app/package.json +++ b/app/package.json @@ -24,6 +24,7 @@ "human-id": "^2.0.1", "i18next": "^19.8.3", "i18next-fs-backend": "^1.0.7", + "json-logic-js": "^2.0.0", "jszip": "^3.6.0", "mime": "^2.4.6", "ms": "^2.1.3", diff --git a/app/server/api/twitch.js b/app/server/api/twitch.js index b32f981a..fb9ad04c 100644 --- a/app/server/api/twitch.js +++ b/app/server/api/twitch.js @@ -1,9 +1,10 @@ -const { events } = require("../../stores"); const loggers = require("../libs/loggers"); const settings = require("../libs/settings"); const state = require("../libs/twitch/state"); const twitchLogin = require("../libs/twitch/login"); const chatJoin = require("../libs/twitch/chat/join"); +const setEvent = require("../libs/twitch/api/setEvent"); +const getEvents = require("../libs/twitch/api/getEvents"); const chatConnect = require("../libs/twitch/chat/connect"); const addCommand = require("../libs/twitch/api/addCommand"); const updateReward = require("../libs/twitch/api/updateReward"); @@ -53,15 +54,8 @@ module.exports = { return result; }); }, - getEvents: () => events.get("events"), - setEvent: (event) => { - events.set( - "events", - events.get("events").map((e) => { - return e.name === event.name ? { ...e, ...event } : e; - }) - ); - }, + getEvents: () => getEvents(), + setEvent: (event) => setEvent(event), getRewardList: () => getRewardList(), getCommandList: () => getCommandList(), getCommandNames: () => getCommandNames(), diff --git a/app/server/libs/twitch/api/getEvents.js b/app/server/libs/twitch/api/getEvents.js new file mode 100644 index 00000000..02163f92 --- /dev/null +++ b/app/server/libs/twitch/api/getEvents.js @@ -0,0 +1,37 @@ +const { events } = require("../../../../stores"); +const api = require("./getUserInfoVars"); + +const eventsWithUserVars = [ + "onCommand", + "onBits", + "onRedemption", + "onAction", + "onBitsBadgeUpgrade", + "onCommunityPayForward", + "onCommunitySub", + "onGiftPaidUpgrade", + "onMessage", + "onPrimeCommunityGift", + "onPrimePaidUpgrade", + "onRaid", + "onResub", + "onRewardGift", + "onRitual", + "onStandardPayForward", + "onSub", + "onSubExtend", + "onSubGift", +]; + +module.exports = function getEvents() { + const userVars = api.getDefaultVars(); + return events.get("events").map((event) => { + if (eventsWithUserVars.includes(event.name)) { + event.tags = { + ...event.tags, + ...userVars, + }; + } + return event; + }); +}; diff --git a/app/server/libs/twitch/api/getUserInfoVars.js b/app/server/libs/twitch/api/getUserInfoVars.js new file mode 100644 index 00000000..be6d035d --- /dev/null +++ b/app/server/libs/twitch/api/getUserInfoVars.js @@ -0,0 +1,60 @@ +const getConnectedUser = require("./getConnectedUser"); +const twitch = require("../index"); + +function getDefaultVars() { + return { + isBroadcaster: "0", + isMod: "0", + isVip: "0", + isSubscriber: "0", + // timestamp: Date.now(), + }; +} + +function getChatUserInfoVars(data) { + const { isBroadcaster, isMod, isSubscriber, isVip } = data.userInfo; + + return { + ...getDefaultVars(), + isBroadcaster, + isMod, + isVip, + isSubscriber, + }; +} + +async function getPubSubUserInfoVars(user) { + const broadcaster = await getConnectedUser(); + const isBroadcaster = broadcaster.login === user.login; + + const isSubscriber = + isBroadcaster || + !!(await twitch.api.helix.subscriptions.getSubscriptionForUser( + broadcaster.id, + user.id + )); + + let isMod = false; + let isVip = false; + + if (!isBroadcaster) { + const mods = await twitch.chat.getMods(broadcaster.login); + const vips = await twitch.chat.getVips(broadcaster.login); + isMod = mods.includes(user.login); + isVip = vips.includes(user.login); + } + + return { + ...getDefaultVars(), + isBroadcaster, + isMod, + isVip, + isSubscriber, + }; +} + +module.exports = { + getDefaultVars, + getChatUserInfoVars, + getPubSubUserInfoVars, +}; diff --git a/app/server/libs/twitch/api/setEvent.js b/app/server/libs/twitch/api/setEvent.js new file mode 100644 index 00000000..c81dc453 --- /dev/null +++ b/app/server/libs/twitch/api/setEvent.js @@ -0,0 +1,10 @@ +const { events } = require("../../../../stores"); + +module.exports = function setEvent(event) { + events.set( + "events", + events.get("events").map((e) => { + return e.name === event.name ? { ...e, ...event } : e; + }) + ); +}; diff --git a/app/server/libs/twitch/chat/events/index.js b/app/server/libs/twitch/chat/events/index.js index e80dc3ff..4cbcfd6a 100644 --- a/app/server/libs/twitch/chat/events/index.js +++ b/app/server/libs/twitch/chat/events/index.js @@ -4,6 +4,8 @@ const fs = require("fs"); fs.readdirSync(__dirname).forEach((filename) => { const name = path.parse(filename).name; + if (name === "index") return; + twitch.chat[name](require(`./${name}`).bind(twitch.chat)); }); diff --git a/app/server/libs/twitch/chat/events/onAction.js b/app/server/libs/twitch/chat/events/onAction.js index 40102d20..71e1e14a 100644 --- a/app/server/libs/twitch/chat/events/onAction.js +++ b/app/server/libs/twitch/chat/events/onAction.js @@ -1,6 +1,7 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onAction(channel, user, message) { - const date = new Intl.DateTimeFormat("fr-FR").format(Date.now()); - pushActions("onAction", { user, message, date }); +module.exports = function onAction(channel, user, message, data) { + const userVars = api.getChatUserInfoVars(data); + pushActions("onAction", { user, message, ...userVars }); }; diff --git a/app/server/libs/twitch/chat/events/onBitsBadgeUpgrade.js b/app/server/libs/twitch/chat/events/onBitsBadgeUpgrade.js index dc808107..ccfbda04 100644 --- a/app/server/libs/twitch/chat/events/onBitsBadgeUpgrade.js +++ b/app/server/libs/twitch/chat/events/onBitsBadgeUpgrade.js @@ -1,6 +1,12 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onBitsBadgeUpgrade(channel, user, upgradeInfo) { +module.exports = function onBitsBadgeUpgrade(channel, user, upgradeInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, threshold } = upgradeInfo; - pushActions("onBitsBadgeUpgrade", { user: displayName, threshold }); + pushActions("onBitsBadgeUpgrade", { + user: displayName, + threshold, + ...userVars, + }); }; diff --git a/app/server/libs/twitch/chat/events/onCommunityPayForward.js b/app/server/libs/twitch/chat/events/onCommunityPayForward.js index 6b00bcef..ee407456 100644 --- a/app/server/libs/twitch/chat/events/onCommunityPayForward.js +++ b/app/server/libs/twitch/chat/events/onCommunityPayForward.js @@ -1,9 +1,17 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onCommunityPayForward(channel, user, forwardInfo) { +module.exports = function onCommunityPayForward( + channel, + user, + forwardInfo, + data +) { + const userVars = api.getChatUserInfoVars(data); const { displayName, originalGifterDisplayName } = forwardInfo; pushActions("onCommunityPayForward", { fromUser: originalGifterDisplayName, toUser: displayName, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onCommunitySub.js b/app/server/libs/twitch/chat/events/onCommunitySub.js index 98b3dadf..25b6efe0 100644 --- a/app/server/libs/twitch/chat/events/onCommunitySub.js +++ b/app/server/libs/twitch/chat/events/onCommunitySub.js @@ -1,11 +1,14 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onCommunitySub(channel, user, subInfo) { +module.exports = function onCommunitySub(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { count, gifter, gifterGiftCount, plan } = subInfo; pushActions("onCommunitySub", { user: gifter, count, total: gifterGiftCount, tiers: plan, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onGiftPaidUpgrade.js b/app/server/libs/twitch/chat/events/onGiftPaidUpgrade.js index 80c23769..385ce705 100644 --- a/app/server/libs/twitch/chat/events/onGiftPaidUpgrade.js +++ b/app/server/libs/twitch/chat/events/onGiftPaidUpgrade.js @@ -1,10 +1,13 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onGiftPaidUpgrade(channel, user, subInfo) { +module.exports = function onGiftPaidUpgrade(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { gifter, displayName, plan } = subInfo; pushActions("onGiftPaidUpgrade", { fromUser: gifter, toUser: displayName, tiers: plan, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onMessage.js b/app/server/libs/twitch/chat/events/onMessage.js index cc8f9379..6a0fd240 100644 --- a/app/server/libs/twitch/chat/events/onMessage.js +++ b/app/server/libs/twitch/chat/events/onMessage.js @@ -1,3 +1,4 @@ +const api = require("../../api/getUserInfoVars"); const pushActions = require("../../pushActions"); const settings = require("../../../settings"); const onCommand = require("../onCommand"); @@ -14,7 +15,8 @@ function parseCommand(prefix, message) { } module.exports = async function onMessage(channel, nick, message, data) { - pushActions("onMessage", { user: nick, message }); + const userVars = api.getChatUserInfoVars(data); + pushActions("onMessage", { user: nick, message, ...userVars }); const prefix = await settings.get("command.prefix"); if (!isCommand(prefix, message)) return; @@ -22,7 +24,7 @@ module.exports = async function onMessage(channel, nick, message, data) { try { const _onCommand = onCommand.bind(this); - await _onCommand({ command, channel, nick, message, data }); + await _onCommand({ command, channel, nick, message, userVars }); } catch (error) { this.say(channel, error.message); } diff --git a/app/server/libs/twitch/chat/events/onPrimeCommunityGift.js b/app/server/libs/twitch/chat/events/onPrimeCommunityGift.js index 558c057e..9a98cb2c 100644 --- a/app/server/libs/twitch/chat/events/onPrimeCommunityGift.js +++ b/app/server/libs/twitch/chat/events/onPrimeCommunityGift.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onPrimeCommunityGift(channel, user, subInfo) { - const { gifter } = subInfo; - pushActions("onPrimeCommunityGift", { user: gifter, name }); +module.exports = function onPrimeCommunityGift(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); + const { gifter, name } = subInfo; + pushActions("onPrimeCommunityGift", { user: gifter, name, ...userVars }); }; diff --git a/app/server/libs/twitch/chat/events/onPrimePaidUpgrade.js b/app/server/libs/twitch/chat/events/onPrimePaidUpgrade.js index eb744198..d1b3afe3 100644 --- a/app/server/libs/twitch/chat/events/onPrimePaidUpgrade.js +++ b/app/server/libs/twitch/chat/events/onPrimePaidUpgrade.js @@ -1,6 +1,12 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onPrimePaidUpgrade(channel, user, subInfo) { +module.exports = function onPrimePaidUpgrade(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, plan } = subInfo; - pushActions("onPrimePaidUpgrade", { user: displayName, tiers: plan }); + pushActions("onPrimePaidUpgrade", { + user: displayName, + tiers: plan, + ...userVars, + }); }; diff --git a/app/server/libs/twitch/chat/events/onRaid.js b/app/server/libs/twitch/chat/events/onRaid.js index 85338637..70acb741 100644 --- a/app/server/libs/twitch/chat/events/onRaid.js +++ b/app/server/libs/twitch/chat/events/onRaid.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onRaid(channel, user, raidInfo) { +module.exports = function onRaid(channel, user, raidInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, viewerCount } = raidInfo; - pushActions("onRaid", { channel: displayName, viewerCount }); + pushActions("onRaid", { channel: displayName, viewerCount, ...userVars }); }; diff --git a/app/server/libs/twitch/chat/events/onResub.js b/app/server/libs/twitch/chat/events/onResub.js index 0f1a1605..dfeb0210 100644 --- a/app/server/libs/twitch/chat/events/onResub.js +++ b/app/server/libs/twitch/chat/events/onResub.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onResub(channel, user, subInfo) { +module.exports = function onResub(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, plan, @@ -19,5 +21,6 @@ module.exports = function onResub(channel, user, subInfo) { months, message, tiers: plan, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onRewardGift.js b/app/server/libs/twitch/chat/events/onRewardGift.js index 80ec60f7..f24edd66 100644 --- a/app/server/libs/twitch/chat/events/onRewardGift.js +++ b/app/server/libs/twitch/chat/events/onRewardGift.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onRewardGift(channel, user, rewardGiftInfo) { +module.exports = function onRewardGift(channel, user, rewardGiftInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { count, domain, @@ -15,5 +17,6 @@ module.exports = function onRewardGift(channel, user, rewardGiftInfo) { type: triggerType, user: gifterDisplayName, total: gifterGiftCount, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onRitual.js b/app/server/libs/twitch/chat/events/onRitual.js index a1f6c26d..38255112 100644 --- a/app/server/libs/twitch/chat/events/onRitual.js +++ b/app/server/libs/twitch/chat/events/onRitual.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onRitual(channel, user, ritualInfo) { +module.exports = function onRitual(channel, user, ritualInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { ritualName, message } = ritualInfo; - pushActions("onRitual", { ritualName, message }); + pushActions("onRitual", { ritualName, message, ...userVars }); }; diff --git a/app/server/libs/twitch/chat/events/onStandardPayForward.js b/app/server/libs/twitch/chat/events/onStandardPayForward.js index 08b78c98..023ee2bc 100644 --- a/app/server/libs/twitch/chat/events/onStandardPayForward.js +++ b/app/server/libs/twitch/chat/events/onStandardPayForward.js @@ -1,6 +1,13 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onStandardPayForward(channel, user, forwardInfo) { +module.exports = function onStandardPayForward( + channel, + user, + forwardInfo, + data +) { + const userVars = api.getChatUserInfoVars(data); const { displayName, recipientDisplayName, @@ -11,5 +18,6 @@ module.exports = function onStandardPayForward(channel, user, forwardInfo) { user: displayName, fromUser: originalGifterDisplayName, toUser: recipientDisplayName, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onSub.js b/app/server/libs/twitch/chat/events/onSub.js index 741b8e57..a9104d6b 100644 --- a/app/server/libs/twitch/chat/events/onSub.js +++ b/app/server/libs/twitch/chat/events/onSub.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onSub(channel, user, subInfo) { +module.exports = function onSub(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, isPrime, @@ -19,5 +21,6 @@ module.exports = function onSub(channel, user, subInfo) { months, message, tiers: plan, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onSubExtend.js b/app/server/libs/twitch/chat/events/onSubExtend.js index f61889ae..026ea634 100644 --- a/app/server/libs/twitch/chat/events/onSubExtend.js +++ b/app/server/libs/twitch/chat/events/onSubExtend.js @@ -1,11 +1,14 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onSubExtend(channel, user, subInfo) { +module.exports = function onSubExtend(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { displayName, endMonth, months, plan } = subInfo; pushActions("onSubExtend", { user: displayName, months, endMonth, tiers: plan, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/events/onSubGift.js b/app/server/libs/twitch/chat/events/onSubGift.js index a5f057f1..0143f381 100644 --- a/app/server/libs/twitch/chat/events/onSubGift.js +++ b/app/server/libs/twitch/chat/events/onSubGift.js @@ -1,6 +1,8 @@ const pushActions = require("../../pushActions"); +const api = require("../../api/getUserInfoVars"); -module.exports = function onSubGift(channel, user, subInfo) { +module.exports = function onSubGift(channel, user, subInfo, data) { + const userVars = api.getChatUserInfoVars(data); const { toUser: displayName, fromUser: gifter, @@ -25,5 +27,6 @@ module.exports = function onSubGift(channel, user, subInfo) { plan, planName, streak, + ...userVars, }); }; diff --git a/app/server/libs/twitch/chat/onCommand.js b/app/server/libs/twitch/chat/onCommand.js index 60cda0b4..f337820e 100644 --- a/app/server/libs/twitch/chat/onCommand.js +++ b/app/server/libs/twitch/chat/onCommand.js @@ -14,7 +14,13 @@ function parseUsage(usage) { .filter((arg) => arg.length); } -module.exports = async function onCommand({ channel, command, nick, message }) { +module.exports = async function onCommand({ + channel, + command, + nick, + message, + userVars, +}) { const commandEntry = await getCommandByName(command.name); if (!commandEntry || !commandEntry.enabled) { @@ -59,7 +65,14 @@ module.exports = async function onCommand({ channel, command, nick, message }) { args.user = nick; cooldowns[command.name] = now; - pushActions("onCommand", { user: nick, message, command, ...args }); + + pushActions("onCommand", { + user: nick, + message, + command, + ...args, + ...userVars, + }); let chatMessage = (commandEntry.message || "").trim(); diff --git a/app/server/libs/twitch/jsonLogic.js b/app/server/libs/twitch/jsonLogic.js new file mode 100644 index 00000000..c9e0e827 --- /dev/null +++ b/app/server/libs/twitch/jsonLogic.js @@ -0,0 +1,14 @@ +const jsonLogic = require("json-logic-js"); + +const lower = (a) => (a + "").toLowerCase(); +const has = (a, b) => lower(a).includes(lower(b)); + +jsonLogic.add_operation("has", has); +jsonLogic.add_operation("!!", (a, b) => !!a === !!b); +jsonLogic.add_operation("hasNot", (a, b) => !has(a, b)); +jsonLogic.add_operation("==", (a, b) => lower(a) == lower(b)); +jsonLogic.add_operation("!=", (a, b) => lower(a) != lower(b)); +jsonLogic.add_operation("^", (a, b) => lower(a).startsWith(lower(b))); +jsonLogic.add_operation("$", (a, b) => lower(a).endsWith(lower(b))); + +module.exports = jsonLogic; diff --git a/app/server/libs/twitch/pubsub/onBits.js b/app/server/libs/twitch/pubsub/onBits.js index a7008624..baa9b226 100644 --- a/app/server/libs/twitch/pubsub/onBits.js +++ b/app/server/libs/twitch/pubsub/onBits.js @@ -1,12 +1,18 @@ +const api = require("../api/getUserInfoVars"); const pushActions = require("../pushActions"); module.exports = async function onBits(message) { const data = message._data.data; + const userVars = await api.getPubSubUserInfoVars({ + login: data.user_name, + id: data.user_id, + }); pushActions("onBits", { user: data.is_anonymous ? "Anonymous" : data.user_name, message: data.chat_message || "", amount: data.bits_used, total: data.total_bits_used, + ...userVars, }); }; diff --git a/app/server/libs/twitch/pubsub/onRedemption.js b/app/server/libs/twitch/pubsub/onRedemption.js index cf895feb..652eba10 100644 --- a/app/server/libs/twitch/pubsub/onRedemption.js +++ b/app/server/libs/twitch/pubsub/onRedemption.js @@ -1,11 +1,15 @@ +const api = require("../api/getUserInfoVars"); const pushActions = require("../pushActions"); module.exports = async function onRedemption(message) { const { id, user, reward, user_input } = message._data.data.redemption; + const userVars = await api.getPubSubUserInfoVars(user); + pushActions("onRedemption", { id, user: user.display_name, reward, message: user_input || "", + ...userVars, }); }; diff --git a/app/server/libs/twitch/pushActions.js b/app/server/libs/twitch/pushActions.js index 73ef026a..a0bb55bd 100644 --- a/app/server/libs/twitch/pushActions.js +++ b/app/server/libs/twitch/pushActions.js @@ -1,4 +1,5 @@ const stores = require("../../../stores"); +const jsonLogic = require("./jsonLogic"); const { push } = require("../actions"); const loggers = require("../loggers"); @@ -25,6 +26,14 @@ function isInvalidReward(event, eventProps) { return eventProps.reward && event.rewardId !== eventProps.reward.id; } +function isInvalidRules(event, eventProps) { + return ( + event.rules && + event.rules.length && + !jsonLogic.apply(event.rules[0], eventProps) + ); +} + function getValidEvents(widget, eventName) { let events = widget.events.filter((event) => event.eventName === eventName); @@ -50,6 +59,7 @@ module.exports = function pushActions(eventName, eventProps) { if (isInvalidReward(event, eventProps)) return; if (isInvalidCommand(event, eventProps)) return; if (isInvalidShortcut(event, eventProps)) return; + if (isInvalidRules(event, eventProps)) return; push({ type, widget, event, eventProps }); }); diff --git a/app/static/locales/en/app.json b/app/static/locales/en/app.json index e8fd4a15..145a2707 100644 --- a/app/static/locales/en/app.json +++ b/app/static/locales/en/app.json @@ -155,7 +155,12 @@ "confirm": "confirm", "export": "export", "import": "import", - "shortcut": "shortcut" + "shortcut": "shortcut", + "conditions": "conditions", + "rule": "rule", + "group": "group", + "and": "and", + "or": "or" }, "obs": { "scene-list": "OBS | Scene list", @@ -249,5 +254,21 @@ "skewY": "skew Y", "perspective": "perspective", "text-shadow": "text shadow" + }, + "condition-builder": { + "rules": { + "==": "equal", + "<": "less", + ">": "greater", + "!=": "not equal", + "<=": "less or equal", + ">=": "greater or equal", + "range<": "range exclusive", + "range<=": "range inclusive", + "^": "starts with", + "$": "ends with", + "has": "has", + "hasNot": "has not" + } } } \ No newline at end of file diff --git a/app/static/locales/es/app.json b/app/static/locales/es/app.json index 931073a9..0c3d85b8 100644 --- a/app/static/locales/es/app.json +++ b/app/static/locales/es/app.json @@ -154,7 +154,12 @@ "confirm": "confirmar", "export": "exportar", "import": "importar", - "shortcut": "shortcut" + "shortcut": "shortcut", + "conditions": "condiciones", + "regla": "regla", + "grupo": "grupo", + "and": "and", + "or": "or" }, "obs": { "scene-list": "OBS | Lista de escenas", @@ -248,5 +253,21 @@ "perspective": "punto de vista", "height": "altura", "text-shadow": "sombra" + }, + "condition-builder": { + "rules": { + "==": "igual", + "<": "menos", + ">": "mayor", + "!=": "No es igual", + "<=": "menos o igual", + ">=": "mayor o igual", + "range<": "range exclusive", + "range<=": "range inclusive", + "^": "comienza con", + "$": "termina con", + "tiene": "tiene", + "hasNot": "no tiene" + } } } \ No newline at end of file diff --git a/app/static/locales/fr/app.json b/app/static/locales/fr/app.json index 7c74580e..580128b9 100644 --- a/app/static/locales/fr/app.json +++ b/app/static/locales/fr/app.json @@ -155,7 +155,12 @@ "confirm": "confirmer", "export": "exporter", "import": "importer", - "shortcut": "shortcut" + "shortcut": "shortcut", + "conditions": "conditions", + "rule": "règle", + "group": "groupe", + "and": "et", + "or": "ou" }, "obs": { "scene-list": "OBS | Liste des scènes", @@ -249,5 +254,21 @@ "perspective": "point de vue", "height": "hauteur", "text-shadow": "ombre" + }, + "condition-builder": { + "rules": { + "==": "égal", + "<": "inférieur", + ">": "supérieur", + "!=": "différent", + "<=": "inférieur ou égal", + ">=": "supérieur ou égal", + "range<": "plage exclusive", + "range<=": "plage inclusive", + "^": "commence par", + "$": "finit par", + "has": "contient", + "hasNot": "contient pas" + } } } \ No newline at end of file diff --git a/app/stores/defaults/events.js b/app/stores/defaults/events.js index 9fdafd1a..5d82bdae 100644 --- a/app/stores/defaults/events.js +++ b/app/stores/defaults/events.js @@ -19,7 +19,10 @@ module.exports = { name: "onBitsBadgeUpgrade", tags: { user: "user", threshold: "threshold" }, }, - { name: "onCommand", tags: {} }, + { + name: "onCommand", + tags: { user: "user" }, + }, { name: "onCommunityPayForward", tags: { fromUser: "fromUser", toUser: "toUser" }, diff --git a/app/stores/migrations/20210228080800-add-event-tags.js b/app/stores/migrations/20210228080800-add-event-tags.js new file mode 100644 index 00000000..d163d60b --- /dev/null +++ b/app/stores/migrations/20210228080800-add-event-tags.js @@ -0,0 +1,16 @@ +const { events: store } = require("../index"); + +module.exports = { + up: async () => { + store.set( + "events", + store.get("events").map((event) => { + if (event.name === "onCommand") { + event.tags = { user: "user" }; + } + return event; + }) + ); + }, + down: async () => {}, +}; diff --git a/app/yarn.lock b/app/yarn.lock index 56d45eba..4d747fe3 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -1368,6 +1368,11 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +json-logic-js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-logic-js/-/json-logic-js-2.0.0.tgz#bd83ade3f1e46e4544062e61f9f7c035052b101d" + integrity sha512-cQBDOXgFtFladCg99wnQ7YfN+nv1+Sznj4K6bp3CTgDJNJKgEXJE2VCXzVBjEU2e1UagDHSek52IQk5Ha38n7Q== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/Action.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/Action.svelte index e5836dec..14b015eb 100644 --- a/front-src/client/components/Panels/Panel/Widget/Edit/Action.svelte +++ b/front-src/client/components/Panels/Panel/Widget/Edit/Action.svelte @@ -38,7 +38,7 @@ eventNames = names .map((val) => ({ key: _(`twitch.events.${val}`), val })) .sort((a, b) => localeSort(a.key, b.key)); - eventNames.unshift({ key: none, val: "none" }); + eventNames.unshift(noneObject); }); $: data = { panel, widget }; @@ -61,17 +61,12 @@ change("component", cloneDeep(widgets[name].config)); } - function onShortcutReset() { - change("shortcutName", ""); - unregisterShortcut(); - } - function onRemoveActionConfirmed({ detail: response }) { removeActionModal = false; response && removeWidgetComponent(panel, widget) .then(() => { - onShortcutReset(); + unregisterShortcut(); change("component", null); }) .catch((error) => { diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ActionEvent.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ActionEvent.svelte index 1eb21920..7814eea8 100644 --- a/front-src/client/components/Panels/Panel/Widget/Edit/ActionEvent.svelte +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ActionEvent.svelte @@ -20,16 +20,14 @@ change("shortcutName", shortcutName); } - function onShortcutReset() { - change("shortcutName", ""); - unregisterShortcut(); + async function onShortcutReset() { + await unregisterShortcut(); + change("shortcutName", null); } - function onChange({ detail }) { - onShortcutReset(); - + async function onChange({ detail }) { if (detail.key === "shortcutName") { - onShortcutChange(detail.value); + await onShortcutChange(detail.value); return; } diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ActionEventSelect.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ActionEventSelect.svelte index 8a22dbf7..bd0aeb99 100644 --- a/front-src/client/components/Panels/Panel/Widget/Edit/ActionEventSelect.svelte +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ActionEventSelect.svelte @@ -17,7 +17,14 @@ const noneObject = { key: none, val: null }; const rewardMap = (reward) => ({ val: reward.id, key: reward.title }); - $: commandNames = [none, ...$commands.map((cmd) => cmd.name)]; + $: commandNames = [ + noneObject, + ...$commands.map((cmd) => ({ + key: cmd.name, + val: cmd.name, + })), + ]; + $: rewardNames = $rewards ? [noneObject, ...$rewards.map(rewardMap)] : [noneObject]; @@ -27,6 +34,7 @@ } function onChange(key, { detail: value }) { + value = value === "null" ? null : value; dispatch("change", { key, value }); } @@ -37,8 +45,9 @@ {#if event.eventName === 'onCommand'} + + diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/RemoveCross.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/RemoveCross.svelte new file mode 100644 index 00000000..b01a495d --- /dev/null +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/RemoveCross.svelte @@ -0,0 +1,12 @@ + + + diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Rule.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Rule.svelte new file mode 100644 index 00000000..447c3e11 --- /dev/null +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Rule.svelte @@ -0,0 +1,137 @@ + + +
+ + {/if} + + {#if isBool} + + {:else if isRange} + + {:else} + + {/if} + +
+ +
diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Select.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Select.svelte new file mode 100644 index 00000000..9711e51d --- /dev/null +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ConditionBuilder/Select.svelte @@ -0,0 +1,14 @@ + + + + diff --git a/front-src/client/components/Panels/Panel/Widget/Edit/ShortcutInput.svelte b/front-src/client/components/Panels/Panel/Widget/Edit/ShortcutInput.svelte index e10f807a..bd985aad 100644 --- a/front-src/client/components/Panels/Panel/Widget/Edit/ShortcutInput.svelte +++ b/front-src/client/components/Panels/Panel/Widget/Edit/ShortcutInput.svelte @@ -69,20 +69,29 @@ }); -
-
+
+
{_('words.shortcut')}
{#if lastShortcut.length} -
-
{getAccelerator(lastShortcut)}
-
+ +