diff --git a/.example.env b/.example.env
index cf8aa10..f68137f 100644
--- a/.example.env
+++ b/.example.env
@@ -6,6 +6,7 @@ CLIENT_SECRET= # Client_secret gotten from the twitch dev console
# REDIRECT_URL= # Redirect URL that has been set in the twitch dev console. Defaults to: http://localhost:3456
# REDIRECT_PORT= # Redirect port if the REDIRECT_URL has not been set. Defaults to 3456. This is also the port the bot will listen on to authenticate
# COMMAND_PREFIX= # The prefix which will be used to activate commands. Defaults to '!'. When requiring a space between prefix and command, escape the space with a backslash
+CHATWIDGET_PORT= # The port that the chat widget will be served on
# The Twitch IDs required below can be gotten from this website: https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
diff --git a/package.json b/package.json
index 638e6bd..222da61 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "qweribot",
- "module": "bot/index.ts",
+ "module": "src/index.ts",
"devDependencies": {
"@types/bun": "latest"
},
diff --git a/bot/auth.ts b/src/auth.ts
similarity index 100%
rename from bot/auth.ts
rename to src/auth.ts
diff --git a/src/chatwidget/index.ts b/src/chatwidget/index.ts
new file mode 100644
index 0000000..c52340b
--- /dev/null
+++ b/src/chatwidget/index.ts
@@ -0,0 +1,72 @@
+import { chatterApi, streamerId } from "..";
+import logger from "../lib/logger";
+import chatWidget from "./www/index.html";
+
+type badgeObject = {
+ [key: string]: {
+ [key: string]: string;
+ };
+};
+
+const port = Number(process.env.CHATWIDGET_PORT);
+if (isNaN(port)) { logger.enverr("CHATWIDGET_PORT"); process.exit(1); };
+
+export default Bun.serve({
+ port,
+ fetch(request, server) {
+ if (server.upgrade(request)) return;
+ return new Response('oops', { status: 500 });
+ },
+ routes: {
+ "/": chatWidget,
+ "/getBadges": async () => {
+ const globalBadges = chatterApi.chat.getGlobalBadges();
+ const channelBadges = chatterApi.chat.getChannelBadges(streamerId);
+ const rawBadges = await Promise.all([globalBadges, channelBadges]);
+
+ const newObj: badgeObject = {};
+ parseRawBadges(newObj, rawBadges[0]);
+ parseRawBadges(newObj, rawBadges[1]);
+
+ return Response.json(newObj);
+ },
+ },
+ websocket: {
+ open(ws) {
+ logger.ok('Opened websocket connection to chatwidget');
+ ws.sendText(JSON.stringify({
+ function: 'serverNotification',
+ message: 'Sucessfully opened websocket connection'
+ }));
+ },
+ message(ws, omessage) {
+ const message = JSON.parse(omessage.toString());
+ if (!message.type) return;
+ switch (message.type) {
+ case 'subscribe':
+ if (!message.target) return;
+ ws.subscribe(message.target);
+ ws.send(JSON.stringify({
+ function: 'serverNotification',
+ message: `Successfully subscribed to all ${message.target} events`
+ }));
+ break;
+ };
+ },
+ close(ws) {
+ ws.close();
+ }
+ },
+ development: true
+});
+
+import { HelixChatBadgeSet } from "@twurple/api";
+
+function parseRawBadges(returnobj: badgeObject, data: HelixChatBadgeSet[]) {
+ for (const badge of data) {
+ if (!returnobj[badge.id]) returnobj[badge.id] = {};
+ for (const version of badge.versions) {
+ returnobj[badge.id]![version.id] = version.getImageUrl(4);
+ };
+ };
+};
diff --git a/src/chatwidget/message.ts b/src/chatwidget/message.ts
new file mode 100644
index 0000000..2483652
--- /dev/null
+++ b/src/chatwidget/message.ts
@@ -0,0 +1,21 @@
+import { EventSubChannelChatMessageEvent, EventSubChannelChatMessageDeleteEvent } from "@twurple/eventsub-base";
+import chatwserver from ".";
+
+export async function addMessageToChatWidget(msg: EventSubChannelChatMessageEvent) {
+ chatwserver.publish('twitch', JSON.stringify({
+ function: 'createMessage',
+ messageParts: msg.messageParts,
+ displayName: msg.chatterDisplayName,
+ chatterId: msg.chatterId,
+ chatterColor: msg.color,
+ messageId: msg.messageId,
+ badgeData: msg.badges
+ }));
+};
+
+export async function deleteMessageFromChatWidget(msg: EventSubChannelChatMessageDeleteEvent) {
+ chatwserver.publish('twitch', JSON.stringify({
+ function: 'deleteMessage',
+ messageId: msg.messageId
+ }));
+};
diff --git a/src/chatwidget/websockettypes.ts b/src/chatwidget/websockettypes.ts
new file mode 100644
index 0000000..65ff4ff
--- /dev/null
+++ b/src/chatwidget/websockettypes.ts
@@ -0,0 +1,55 @@
+export type createMessageEvent = { function: 'createMessage', messageParts: EventSubChatMessagePart[], messageId: string, displayName: string, chatterId: string, chatterColor: null | string, badgeData: string[] };
+export type deleteMessageEvent = { function: 'deleteMessage', messageId: string };
+export type serverNotificationEvent = { function: 'serverNotification', message: string };
+
+export type eventData = createMessageEvent | deleteMessageEvent | serverNotificationEvent;
+
+// The types below are taken straight from @twurple/eventsub-base
+// I would import this from the package, but that's impossible
+export interface EventSubChatMessageTextPart {
+ type: 'text';
+ text: string;
+}
+
+export interface EventSubChatMessageCheermote {
+ prefix: string;
+ bits: number;
+ tier: number;
+}
+
+export interface EventSubChatMessageCheermotePart {
+ type: 'cheermote';
+ text: string;
+ cheermote: EventSubChatMessageCheermote;
+}
+
+export interface EventSubChatMessageEmote {
+ id: string;
+ emote_set_id: string;
+ owner_id: string;
+ format: string[];
+}
+
+export interface EventSubChatMessageEmotePart {
+ type: 'emote';
+ text: string;
+ emote: EventSubChatMessageEmote;
+}
+
+export interface EventSubChatMessageMention {
+ user_id: string;
+ user_name: string;
+ user_login: string;
+}
+
+export interface EventSubChatMessageMentionPart {
+ type: 'mention';
+ text: string;
+ mention: EventSubChatMessageMention;
+}
+
+export type EventSubChatMessagePart =
+ | EventSubChatMessageTextPart
+ | EventSubChatMessageCheermotePart
+ | EventSubChatMessageEmotePart
+ | EventSubChatMessageMentionPart;
diff --git a/src/chatwidget/www/index.html b/src/chatwidget/www/index.html
new file mode 100644
index 0000000..25b02f3
--- /dev/null
+++ b/src/chatwidget/www/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ qwerinope's chat widget
+
+
+
+
+
+
diff --git a/src/chatwidget/www/src/createMessage.ts b/src/chatwidget/www/src/createMessage.ts
new file mode 100644
index 0000000..213e3c5
--- /dev/null
+++ b/src/chatwidget/www/src/createMessage.ts
@@ -0,0 +1,61 @@
+import './style.css';
+
+const badges = await fetch(`http://${location.host}/getBadges`).then(data => data.json());
+
+import { type createMessageEvent } from '../../websockettypes';
+
+export function parseMessage(data: createMessageEvent): HTMLDivElement {
+ const parentDiv = document.createElement('div');
+ parentDiv.className = 'message';
+
+ // Badge Parsing
+ const badgeContainer = document.createElement('div');
+ badgeContainer.className = 'badgeContainer';
+ for (const badge of Object.entries(data.badgeData)) {
+ const badgeElement = document.createElement('img');
+ badgeElement.className = 'badgeElement';
+ const currentbadge = badges[badge[0]][badge[1]];
+ badgeElement.src = currentbadge;
+ badgeContainer.appendChild(badgeElement);
+ };
+ parentDiv.appendChild(badgeContainer);
+
+ const chatterName = document.createElement('span');
+ chatterName.style = `color: ${data.chatterColor ?? "#00ff00"}`;
+ chatterName.innerText = data.displayName;
+ chatterName.className = "chatterName";
+ parentDiv.appendChild(chatterName);
+
+ const seperator = document.createElement('span');
+ seperator.innerText = ": ";
+ seperator.className = "chatMessageSeparator";
+ parentDiv.appendChild(seperator);
+
+ const textElement = document.createElement('div');
+ for (const messagePart of data.messageParts) {
+ let messageElement;
+ switch (messagePart.type) {
+ case 'text':
+ messageElement = document.createElement('span');
+ messageElement.className = "textMessage";
+ messageElement.innerText = messagePart.text;
+ break;
+ case 'cheermote':
+ messageElement = document.createElement('img');
+ break;
+ case 'emote':
+ messageElement = document.createElement('img');
+ messageElement.className = "emoteMessage";
+ messageElement.src = `https://static-cdn.jtvnw.net/emoticons/v2/${messagePart.emote.id}/default/dark/3.0`;
+ break;
+ case 'mention':
+ messageElement = document.createElement('span');
+ messageElement.innerText = `Replying to ${messagePart.text}`;
+ messageElement.className = "replyMessage";
+ break;
+ };
+ textElement.appendChild(messageElement);
+ };
+ parentDiv.appendChild(textElement);
+ return parentDiv;
+};
diff --git a/src/chatwidget/www/src/main.ts b/src/chatwidget/www/src/main.ts
new file mode 100644
index 0000000..8b1cf6f
--- /dev/null
+++ b/src/chatwidget/www/src/main.ts
@@ -0,0 +1,33 @@
+import { type eventData } from "../../websockettypes";
+
+import { parseMessage } from './createMessage';
+
+const socket = new WebSocket(`ws://${location.host}`);
+
+socket.onopen = () => {
+ socket.send(JSON.stringify({
+ type: 'subscribe',
+ target: 'twitch'
+ }));
+};
+
+socket.onmessage = event => {
+ const data: eventData = JSON.parse(event.data);
+ switch (data.function) {
+ case 'createMessage':
+ const newMessageElement = parseMessage(data);
+ newMessageElement.id = data.messageId;
+ document.querySelector("#message-container")?.appendChild(newMessageElement);
+ break;
+ case 'deleteMessage':
+ document.querySelector(`#${CSS.escape(data.messageId)}`)?.remove();
+ break;
+ case 'serverNotification':
+ console.log(data.message);
+ break;
+ };
+};
+
+document.querySelector('#app')!.innerHTML = `
+
+`;
diff --git a/src/chatwidget/www/src/style.css b/src/chatwidget/www/src/style.css
new file mode 100644
index 0000000..b394652
--- /dev/null
+++ b/src/chatwidget/www/src/style.css
@@ -0,0 +1,46 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+}
+
+.message {
+ >* {
+ display: inline;
+ }
+
+ .replymessage {
+ color: grey;
+ font-size: 2px;
+ }
+
+ padding: 2px;
+}
+
+img {
+ vertical-align: middle;
+ width: 18px;
+ padding: 1px;
+}
diff --git a/src/chatwidget/www/tsconfig.json b/src/chatwidget/www/tsconfig.json
new file mode 100644
index 0000000..c56e058
--- /dev/null
+++ b/src/chatwidget/www/tsconfig.json
@@ -0,0 +1,105 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ /* Language and Environment */
+ "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "libReplacement": true, /* Enable lib replacement. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+ /* Modules */
+ "module": "esnext", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/bot/cheers/index.ts b/src/cheers/index.ts
similarity index 100%
rename from bot/cheers/index.ts
rename to src/cheers/index.ts
diff --git a/bot/cheers/timeout.ts b/src/cheers/timeout.ts
similarity index 100%
rename from bot/cheers/timeout.ts
rename to src/cheers/timeout.ts
diff --git a/bot/commands/addadmin.ts b/src/commands/addadmin.ts
similarity index 100%
rename from bot/commands/addadmin.ts
rename to src/commands/addadmin.ts
diff --git a/bot/commands/admindonate.ts b/src/commands/admindonate.ts
similarity index 100%
rename from bot/commands/admindonate.ts
rename to src/commands/admindonate.ts
diff --git a/bot/commands/admingive.ts b/src/commands/admingive.ts
similarity index 100%
rename from bot/commands/admingive.ts
rename to src/commands/admingive.ts
diff --git a/bot/commands/disablecheer.ts b/src/commands/disablecheer.ts
similarity index 100%
rename from bot/commands/disablecheer.ts
rename to src/commands/disablecheer.ts
diff --git a/bot/commands/disablecommand.ts b/src/commands/disablecommand.ts
similarity index 100%
rename from bot/commands/disablecommand.ts
rename to src/commands/disablecommand.ts
diff --git a/bot/commands/donateqbucks.ts b/src/commands/donateqbucks.ts
similarity index 100%
rename from bot/commands/donateqbucks.ts
rename to src/commands/donateqbucks.ts
diff --git a/bot/commands/enablecheer.ts b/src/commands/enablecheer.ts
similarity index 100%
rename from bot/commands/enablecheer.ts
rename to src/commands/enablecheer.ts
diff --git a/bot/commands/enablecommand.ts b/src/commands/enablecommand.ts
similarity index 100%
rename from bot/commands/enablecommand.ts
rename to src/commands/enablecommand.ts
diff --git a/bot/commands/getadmins.ts b/src/commands/getadmins.ts
similarity index 100%
rename from bot/commands/getadmins.ts
rename to src/commands/getadmins.ts
diff --git a/bot/commands/getbalance.ts b/src/commands/getbalance.ts
similarity index 100%
rename from bot/commands/getbalance.ts
rename to src/commands/getbalance.ts
diff --git a/bot/commands/getcheers.ts b/src/commands/getcheers.ts
similarity index 100%
rename from bot/commands/getcheers.ts
rename to src/commands/getcheers.ts
diff --git a/bot/commands/getcommands.ts b/src/commands/getcommands.ts
similarity index 100%
rename from bot/commands/getcommands.ts
rename to src/commands/getcommands.ts
diff --git a/bot/commands/getinventory.ts b/src/commands/getinventory.ts
similarity index 100%
rename from bot/commands/getinventory.ts
rename to src/commands/getinventory.ts
diff --git a/bot/commands/gettimeout.ts b/src/commands/gettimeout.ts
similarity index 100%
rename from bot/commands/gettimeout.ts
rename to src/commands/gettimeout.ts
diff --git a/bot/commands/giveitem.ts b/src/commands/giveitem.ts
similarity index 100%
rename from bot/commands/giveitem.ts
rename to src/commands/giveitem.ts
diff --git a/bot/commands/index.ts b/src/commands/index.ts
similarity index 100%
rename from bot/commands/index.ts
rename to src/commands/index.ts
diff --git a/bot/commands/iteminfo.ts b/src/commands/iteminfo.ts
similarity index 100%
rename from bot/commands/iteminfo.ts
rename to src/commands/iteminfo.ts
diff --git a/bot/commands/itemlock.ts b/src/commands/itemlock.ts
similarity index 100%
rename from bot/commands/itemlock.ts
rename to src/commands/itemlock.ts
diff --git a/bot/commands/ping.ts b/src/commands/ping.ts
similarity index 100%
rename from bot/commands/ping.ts
rename to src/commands/ping.ts
diff --git a/bot/commands/removeadmin.ts b/src/commands/removeadmin.ts
similarity index 100%
rename from bot/commands/removeadmin.ts
rename to src/commands/removeadmin.ts
diff --git a/bot/commands/seiso.ts b/src/commands/seiso.ts
similarity index 100%
rename from bot/commands/seiso.ts
rename to src/commands/seiso.ts
diff --git a/bot/commands/testcheer.ts b/src/commands/testcheer.ts
similarity index 100%
rename from bot/commands/testcheer.ts
rename to src/commands/testcheer.ts
diff --git a/bot/commands/useitem.ts b/src/commands/useitem.ts
similarity index 100%
rename from bot/commands/useitem.ts
rename to src/commands/useitem.ts
diff --git a/bot/commands/vulnchatters.ts b/src/commands/vulnchatters.ts
similarity index 100%
rename from bot/commands/vulnchatters.ts
rename to src/commands/vulnchatters.ts
diff --git a/bot/commands/yabai.ts b/src/commands/yabai.ts
similarity index 100%
rename from bot/commands/yabai.ts
rename to src/commands/yabai.ts
diff --git a/bot/db/connection.ts b/src/db/connection.ts
similarity index 100%
rename from bot/db/connection.ts
rename to src/db/connection.ts
diff --git a/bot/db/dbAuth.ts b/src/db/dbAuth.ts
similarity index 100%
rename from bot/db/dbAuth.ts
rename to src/db/dbAuth.ts
diff --git a/bot/db/dbTimeouts.ts b/src/db/dbTimeouts.ts
similarity index 100%
rename from bot/db/dbTimeouts.ts
rename to src/db/dbTimeouts.ts
diff --git a/bot/db/dbUsedItems.ts b/src/db/dbUsedItems.ts
similarity index 100%
rename from bot/db/dbUsedItems.ts
rename to src/db/dbUsedItems.ts
diff --git a/bot/db/dbUser.ts b/src/db/dbUser.ts
similarity index 100%
rename from bot/db/dbUser.ts
rename to src/db/dbUser.ts
diff --git a/src/events/deleteMessage.ts b/src/events/deleteMessage.ts
new file mode 100644
index 0000000..d98d622
--- /dev/null
+++ b/src/events/deleteMessage.ts
@@ -0,0 +1,6 @@
+import { eventSub, streamerId } from "..";
+import { deleteMessageFromChatWidget } from "../chatwidget/message";
+
+eventSub.onChannelChatMessageDelete(streamerId, streamerId, async msg => {
+ deleteMessageFromChatWidget(msg);
+});
diff --git a/bot/events/index.ts b/src/events/index.ts
similarity index 100%
rename from bot/events/index.ts
rename to src/events/index.ts
diff --git a/bot/events/message.ts b/src/events/message.ts
similarity index 96%
rename from bot/events/message.ts
rename to src/events/message.ts
index 1414897..4300772 100644
--- a/bot/events/message.ts
+++ b/src/events/message.ts
@@ -6,12 +6,14 @@ import { redis } from "bun";
import { isAdmin } from "../lib/admins";
import cheers from "../cheers";
import logger from "../lib/logger";
+import { addMessageToChatWidget } from "../chatwidget/message";
logger.info(`Loaded the following commands: ${commands.keys().toArray().join(', ')}`);
eventSub.onChannelChatMessage(streamerId, streamerId, parseChatMessage);
async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
+ addMessageToChatWidget(msg);
if (!singleUserMode && msg.chatterId === chatterId) return;
// return if double user mode is on and the chatter says something, we don't need them
diff --git a/bot/index.ts b/src/index.ts
similarity index 98%
rename from bot/index.ts
rename to src/index.ts
index 9970605..b273a5d 100644
--- a/bot/index.ts
+++ b/src/index.ts
@@ -31,3 +31,5 @@ export const streamerUsers = [chatterId, streamerId];
streamerUsers.forEach(async id => await addAdmin(id));
await import("./events");
+
+await import("./chatwidget");
diff --git a/bot/items/blaster.ts b/src/items/blaster.ts
similarity index 100%
rename from bot/items/blaster.ts
rename to src/items/blaster.ts
diff --git a/bot/items/grenade.ts b/src/items/grenade.ts
similarity index 100%
rename from bot/items/grenade.ts
rename to src/items/grenade.ts
diff --git a/bot/items/index.ts b/src/items/index.ts
similarity index 100%
rename from bot/items/index.ts
rename to src/items/index.ts
diff --git a/bot/items/silverbullet.ts b/src/items/silverbullet.ts
similarity index 100%
rename from bot/items/silverbullet.ts
rename to src/items/silverbullet.ts
diff --git a/bot/items/tnt.ts b/src/items/tnt.ts
similarity index 100%
rename from bot/items/tnt.ts
rename to src/items/tnt.ts
diff --git a/bot/lib/admins.ts b/src/lib/admins.ts
similarity index 100%
rename from bot/lib/admins.ts
rename to src/lib/admins.ts
diff --git a/bot/lib/changeBalance.ts b/src/lib/changeBalance.ts
similarity index 100%
rename from bot/lib/changeBalance.ts
rename to src/lib/changeBalance.ts
diff --git a/bot/lib/dateManager.ts b/src/lib/dateManager.ts
similarity index 100%
rename from bot/lib/dateManager.ts
rename to src/lib/dateManager.ts
diff --git a/bot/lib/logger.ts b/src/lib/logger.ts
similarity index 100%
rename from bot/lib/logger.ts
rename to src/lib/logger.ts
diff --git a/bot/lib/parseCommandArgs.ts b/src/lib/parseCommandArgs.ts
similarity index 100%
rename from bot/lib/parseCommandArgs.ts
rename to src/lib/parseCommandArgs.ts
diff --git a/bot/lib/timeout.ts b/src/lib/timeout.ts
similarity index 100%
rename from bot/lib/timeout.ts
rename to src/lib/timeout.ts
diff --git a/bot/user.ts b/src/user.ts
similarity index 100%
rename from bot/user.ts
rename to src/user.ts