├── Procfile ├── design ├── patch notes ├── icon.png └── icon.sketch ├── dist ├── @types │ └── command.js ├── src │ ├── scripts │ │ ├── defaultServerSettings.js │ │ ├── timezoneCodeToLocationData.js │ │ └── memo.js │ ├── events │ │ ├── kickedFromServer.js │ │ ├── receiveGuildMessage.js │ │ ├── otherMemberLeaveServer.js │ │ ├── receivePrivateMessage.js │ │ └── addedToServer.js │ ├── commands │ │ ├── here.js │ │ ├── count.js │ │ ├── support.js │ │ ├── invite.js │ │ ├── format.js │ │ ├── toggledeletecommand.js │ │ ├── suppresswarnings.js │ │ ├── toggleautorespond.js │ │ ├── toggleadminonly.js │ │ ├── verboseAll.js │ │ ├── removeme.js │ │ ├── timein.js │ │ ├── me.js │ │ ├── role.js │ │ ├── deleteresponse.js │ │ ├── removeuser.js │ │ ├── setRepeatAnnounceTime.js │ │ ├── setuser.js │ │ ├── setprefix.js │ │ ├── set.js │ │ ├── stamp.js │ │ ├── time.js │ │ └── index.js │ ├── db │ │ ├── firestore.js │ │ └── location.js │ ├── actions │ │ ├── contactGuildAdmin.js │ │ ├── getTimezoneFromLocation.js │ │ ├── replyInChannel.js │ │ └── replyToAts.js │ └── index.js ├── scripts │ ├── defaultServerSettings.js │ ├── timezoneCodeToLocationData.js │ └── memo.js ├── events │ ├── kickedFromServer.js │ ├── receiveGuildMessage.js │ ├── otherMemberLeaveServer.js │ ├── receivePrivateMessage.js │ └── addedToServer.js ├── commands │ ├── here.js │ ├── count.js │ ├── support.js │ ├── invite.js │ ├── format.js │ ├── toggledeletecommand.js │ ├── toggleautorespond.js │ ├── suppresswarnings.js │ ├── verboseAll.js │ ├── toggleadminonly.js │ ├── removeme.js │ ├── timein.js │ ├── deleteresponse.js │ ├── me.js │ ├── removeuser.js │ ├── setRepeatAnnounceTime.js │ ├── role.js │ ├── setuser.js │ ├── setprefix.js │ ├── set.js │ ├── stamp.js │ └── time.js ├── db │ ├── firestore.js │ └── location.js ├── actions │ ├── contactGuildAdmin.js │ ├── getTimezoneFromLocation.js │ ├── replyInChannel.js │ └── replyToAts.js ├── index.js └── bot.js ├── .gitignore ├── .env_example ├── timezonebot-pm2.json ├── @types ├── timezone.d.ts ├── settings.d.ts └── command.d.ts ├── src ├── scripts │ ├── defaultServerSettings.ts │ ├── timezoneCodeToLocationData.ts │ ├── memo.ts │ └── timeFromString.ts ├── events │ ├── otherMemberLeaveServer.ts │ ├── receiveGuildMessage.ts │ ├── kickedFromServer.ts │ ├── receivePrivateMessage.ts │ └── addedToServer.ts ├── commands │ ├── here.ts │ ├── count.ts │ ├── support.ts │ ├── invite.ts │ ├── format.ts │ ├── toggledeletecommand.ts │ ├── suppresswarnings.ts │ ├── toggleautorespond.ts │ ├── toggleadminonly.ts │ ├── removeme.ts │ ├── verboseAll.ts │ ├── deleteresponse.ts │ ├── me.ts │ ├── timein.ts │ ├── removeuser.ts │ ├── role.ts │ ├── setRepeatAnnounceTime.ts │ ├── setuser.ts │ ├── setprefix.ts │ ├── stamp.ts │ ├── set.ts │ ├── index.ts │ └── time.ts ├── db │ ├── firestore.ts │ └── location.ts ├── index.ts ├── actions │ ├── contactGuildAdmin.ts │ ├── getTimezoneFromLocation.ts │ └── replyInChannel.ts └── bot.ts ├── tsconfig.json ├── package.json ├── README.md └── instructions selfhosting.md /Procfile: -------------------------------------------------------------------------------- 1 | worker: npm start -------------------------------------------------------------------------------- /design/patch notes: -------------------------------------------------------------------------------- 1 | t!count -------------------------------------------------------------------------------- /design/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midblue/timezone-bot/HEAD/design/icon.png -------------------------------------------------------------------------------- /design/icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midblue/timezone-bot/HEAD/design/icon.sketch -------------------------------------------------------------------------------- /dist/@types/command.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | data/users.json 4 | redis 5 | dump.rdb 6 | .DS_Store 7 | nicethings.txt 8 | digitalocean.txt -------------------------------------------------------------------------------- /.env_example: -------------------------------------------------------------------------------- 1 | DISCORD_TOKEN= 2 | GOOGLE_API_KEY= 3 | BOT_ID= 4 | FIREBASE_PROJECT_ID= 5 | FIREBASE_CLIENT_EMAIL= 6 | FIREBASE_PRIVATE_KEY= -------------------------------------------------------------------------------- /timezonebot-pm2.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "Timezone Bot", 4 | "cwd": "./", 5 | "script": "index.js" 6 | }] 7 | } 8 | -------------------------------------------------------------------------------- /@types/timezone.d.ts: -------------------------------------------------------------------------------- 1 | interface LocationData { 2 | location: string 3 | timezoneName: string 4 | } 5 | interface LocationDataWithUtc extends LocationData { 6 | utcOffset: string 7 | } 8 | 9 | interface UserData { 10 | location: string 11 | timezoneName: string 12 | } 13 | -------------------------------------------------------------------------------- /src/scripts/defaultServerSettings.ts: -------------------------------------------------------------------------------- 1 | const settings: Settings = { 2 | prefix: `t!`, 3 | autoRespond: true, 4 | adminOnly: false, 5 | deleteCommand: false, 6 | deleteResponse: false, 7 | repeatAnnounceTime: 30, 8 | format24: true, 9 | verboseAll: false, 10 | } 11 | export default settings 12 | -------------------------------------------------------------------------------- /@types/settings.d.ts: -------------------------------------------------------------------------------- 1 | interface Settings { 2 | guildId?: string 3 | prefix?: string 4 | autoRespond?: boolean 5 | adminOnly?: boolean 6 | deleteCommand?: boolean 7 | deleteResponse?: number | false 8 | suppressWarnings?: boolean 9 | format24?: boolean 10 | repeatAnnounceTime?: number 11 | verboseAll?: boolean 12 | } 13 | -------------------------------------------------------------------------------- /src/events/otherMemberLeaveServer.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import * as Discord from 'discord.js-light' 3 | 4 | export default async ( 5 | member: Discord.GuildMember | Discord.PartialGuildMember, 6 | ) => { 7 | const guildId = member.guild.id 8 | const userId = member.id || member.user?.id 9 | db.removeUserFromGuild({ guildId, userId }) 10 | } 11 | -------------------------------------------------------------------------------- /@types/command.d.ts: -------------------------------------------------------------------------------- 1 | import Discord from 'discord.js-light' 2 | 3 | interface ActionProps { 4 | msg: Discord.Message 5 | settings: Settings 6 | match: string[] 7 | here?: boolean 8 | users?: Discord.GuildMember[] 9 | prependText?: string 10 | count?: true 11 | senderIsAdmin?: boolean 12 | typedUser?: Discord.GuildMember 13 | client?: Discord.Client 14 | } 15 | -------------------------------------------------------------------------------- /dist/src/scripts/defaultServerSettings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.default = { 4 | prefix: `t!`, 5 | autoRespond: true, 6 | adminOnly: false, 7 | deleteCommand: false, 8 | deleteResponse: false, 9 | repeatAnnounceTime: 30, 10 | format24: true, 11 | verboseAll: false, 12 | }; 13 | -------------------------------------------------------------------------------- /dist/src/events/kickedFromServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.default = (guild) => { 4 | // seems like there's no reason to delete their settings, they might readd later 5 | // db.removeGuild({ guildId: guild.id }) 6 | console.log(`< < < < < < Was removed from a guild: ` + 7 | guild.name); 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "allowJs": true, 5 | "target": "ES2019", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "skipLibCheck": true 12 | }, 13 | "include": ["./src/**/*", "@types"] 14 | } 15 | -------------------------------------------------------------------------------- /src/events/receiveGuildMessage.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import * as Discord from 'discord.js-light' 3 | const commands = require(`../commands/index`) 4 | 5 | export default async ( 6 | msg: Discord.Message, 7 | client: Discord.Client, 8 | ) => { 9 | await commands( 10 | msg, 11 | await db.getGuildSettings({ guildId: msg.guild?.id }), 12 | client, 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /dist/scripts/defaultServerSettings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const settings = { 4 | prefix: `t!`, 5 | autoRespond: true, 6 | adminOnly: false, 7 | deleteCommand: false, 8 | deleteResponse: false, 9 | repeatAnnounceTime: 30, 10 | format24: true, 11 | verboseAll: false, 12 | }; 13 | exports.default = settings; 14 | -------------------------------------------------------------------------------- /dist/events/kickedFromServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.default = (guild) => { 4 | // seems like there's no reason to delete their settings, they might readd later 5 | // db.removeGuild({ guildId: guild.id }) 6 | if (guild.name) 7 | console.log(`< < < < < < Was removed from a guild: ` + 8 | guild.name); 9 | }; 10 | -------------------------------------------------------------------------------- /src/commands/here.ts: -------------------------------------------------------------------------------- 1 | import allCommand from './all' 2 | import type { ActionProps } from '../../@types/command' 3 | 4 | export default { 5 | regex(settings: Settings) { 6 | return new RegExp( 7 | `^${settings.prefix}(?:here|h)$`, 8 | `gi`, 9 | ) 10 | }, 11 | async action({ msg, settings, match }: ActionProps) { 12 | allCommand.action({ msg, settings, match, here: true }) 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /src/commands/count.ts: -------------------------------------------------------------------------------- 1 | import allCommand from './all' 2 | import type { ActionProps } from '../../@types/command' 3 | 4 | export default { 5 | regex(settings: Settings) { 6 | return new RegExp( 7 | `^${settings.prefix}(?:counts?|c) ?(.*)?$`, 8 | `gi`, 9 | ) 10 | }, 11 | async action({ msg, settings, match }: ActionProps) { 12 | allCommand.action({ msg, settings, match, count: true }) 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /src/events/kickedFromServer.ts: -------------------------------------------------------------------------------- 1 | // const db = require('../db/firestore') 2 | import * as Discord from 'discord.js-light' 3 | 4 | export default (guild: Discord.Guild) => { 5 | // seems like there's no reason to delete their settings, they might readd later 6 | // db.removeGuild({ guildId: guild.id }) 7 | if (guild.name) 8 | console.log( 9 | `< < < < < < Was removed from a guild: ` + 10 | guild.name, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/db/firestore.ts: -------------------------------------------------------------------------------- 1 | import admin from 'firebase-admin' 2 | /* eslint-disable camelcase */ 3 | 4 | admin.initializeApp({ 5 | credential: admin.credential.cert({ 6 | project_id: process.env.FIREBASE_PROJECT_ID!, 7 | client_email: process.env.FIREBASE_CLIENT_EMAIL!, 8 | private_key: process.env.FIREBASE_PRIVATE_KEY!.replace( 9 | /\\n/g, 10 | `\n`, 11 | ), 12 | } as admin.ServiceAccount), 13 | }) 14 | 15 | import guild from './guild' 16 | import location from './location' 17 | 18 | export default { 19 | ...guild, 20 | ...location, 21 | } 22 | -------------------------------------------------------------------------------- /dist/commands/here.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const all_1 = __importDefault(require("./all")); 7 | exports.default = { 8 | regex(settings) { 9 | return new RegExp(`^${settings.prefix}(?:here|h)$`, `gi`); 10 | }, 11 | async action({ msg, settings, match }) { 12 | all_1.default.action({ msg, settings, match, here: true }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dist/src/commands/here.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const all_1 = __importDefault(require("./all")); 7 | module.exports = { 8 | regex(settings) { 9 | return new RegExp(`^${settings.prefix}(?:here|h)$`, `gi`); 10 | }, 11 | async action({ msg, settings, match }) { 12 | all_1.default.action({ msg, settings, match, here: true }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dist/commands/count.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const all_1 = __importDefault(require("./all")); 7 | exports.default = { 8 | regex(settings) { 9 | return new RegExp(`^${settings.prefix}(?:counts?|c) ?(.*)?$`, `gi`); 10 | }, 11 | async action({ msg, settings, match }) { 12 | all_1.default.action({ msg, settings, match, count: true }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dist/src/commands/count.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const all_1 = __importDefault(require("./all")); 7 | exports.default = { 8 | regex(settings) { 9 | return new RegExp(`^${settings.prefix}(?:counts?|c) ?(.*)?$`, `gi`); 10 | }, 11 | async action({ msg, settings, match }) { 12 | all_1.default.action({ msg, settings, match, count: true }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dist/events/receiveGuildMessage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commands = require(`../commands/index`); 8 | exports.default = async (msg, client) => { 9 | var _a; 10 | await commands(msg, await firestore_1.default.getGuildSettings({ guildId: (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id }), client); 11 | }; 12 | -------------------------------------------------------------------------------- /dist/events/otherMemberLeaveServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | exports.default = async (member) => { 8 | var _a; 9 | const guildId = member.guild.id; 10 | const userId = member.id || ((_a = member.user) === null || _a === void 0 ? void 0 : _a.id); 11 | firestore_1.default.removeUserFromGuild({ guildId, userId }); 12 | }; 13 | -------------------------------------------------------------------------------- /dist/src/events/receiveGuildMessage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commands = require(`../commands/index`); 8 | exports.default = async (msg, client) => { 9 | var _a; 10 | await commands(msg, await firestore_1.default.getGuildSettings({ guildId: (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id }), client); 11 | }; 12 | -------------------------------------------------------------------------------- /dist/src/events/otherMemberLeaveServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | exports.default = async (member) => { 8 | var _a; 9 | const guildId = member.guild.id; 10 | const memberId = member.id || ((_a = member.user) === null || _a === void 0 ? void 0 : _a.id); 11 | firestore_1.default.removeUserFromGuild(guildId, memberId); 12 | }; 13 | -------------------------------------------------------------------------------- /src/events/receivePrivateMessage.ts: -------------------------------------------------------------------------------- 1 | import timeIn from '../commands/timein' 2 | import * as Discord from 'discord.js-light' 3 | const defaultSettings = require(`../scripts/defaultServerSettings`) 4 | 5 | export default async (msg: Discord.Message) => { 6 | await timeIn.action({ 7 | msg, 8 | settings: defaultSettings, 9 | match: [``, ` `, msg.content], 10 | }) 11 | 12 | // msg.channel.send(`I only work in a server channel for now. 13 | 14 | // If you're looking for the invite link, it's https://discord.com/api/oauth2/authorize?client_id=723017262369472603&permissions=75840&scope=bot`) 15 | } 16 | -------------------------------------------------------------------------------- /dist/commands/support.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const replyInChannel_1 = require("../actions/replyInChannel"); 4 | exports.default = { 5 | regex(settings) { 6 | return new RegExp(`^${settings.prefix}(?:support)$`, `gi`); 7 | }, 8 | async action({ msg, settings, match }) { 9 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Support (${msg.author.username}) `); 10 | (0, replyInChannel_1.send)(msg, `Join the TimezoneBot Support server here: `, `none`, settings); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /dist/src/commands/support.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const replyInChannel_1 = require("../actions/replyInChannel"); 4 | module.exports = { 5 | regex(settings) { 6 | return new RegExp(`^${settings.prefix}(?:support)$`, `gi`); 7 | }, 8 | async action({ msg, settings, match }) { 9 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Support (${msg.author.username}) `); 10 | (0, replyInChannel_1.send)(msg, `Join the TimezoneBot Support server here: `, `none`, settings); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /dist/commands/invite.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const replyInChannel_1 = require("../actions/replyInChannel"); 4 | exports.default = { 5 | regex(settings) { 6 | return new RegExp(`^${settings.prefix}(?:invite)$`, `gi`); 7 | }, 8 | async action({ msg, settings, match }) { 9 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Invite link (${msg.author.username}) `); 10 | (0, replyInChannel_1.send)(msg, `The bot invite link is `, `none`, settings); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /dist/src/commands/invite.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const replyInChannel_1 = require("../actions/replyInChannel"); 4 | module.exports = { 5 | regex(settings) { 6 | return new RegExp(`^${settings.prefix}(?:invite)$`, `gi`); 7 | }, 8 | async action({ msg, settings, match }) { 9 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Invite link (${msg.author.username}) `); 10 | (0, replyInChannel_1.send)(msg, `The bot invite link is `, `none`, settings); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/events/addedToServer.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import * as Discord from 'discord.js-light' 3 | import { getGuildMembers } from '../scripts/commonFunctions' 4 | 5 | export default async (guild: Discord.Guild) => { 6 | if (await db.hasGuild({ guildId: guild.id })) 7 | return console.log( 8 | `> > > > > > Was re-added to a guild:`, 9 | guild.name, 10 | guild.id, 11 | ) 12 | 13 | await db.addGuild({ 14 | guildId: guild.id, 15 | guildName: guild.name, 16 | }) 17 | console.log( 18 | `> > > > > > Was added to a new guild:`, 19 | guild.name, 20 | guild.id, 21 | `(${(await getGuildMembers({ guild })).length} users)`, 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/commands/support.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | regex(settings: Settings) { 7 | return new RegExp( 8 | `^${settings.prefix}(?:support)$`, 9 | `gi`, 10 | ) 11 | }, 12 | async action({ msg, settings, match }: ActionProps) { 13 | console.log( 14 | `${ 15 | msg.guild ? msg.guild.name : `Private Message` 16 | } - Support (${msg.author.username}) `, 17 | ) 18 | 19 | send( 20 | msg, 21 | `Join the TimezoneBot Support server here: `, 22 | `none`, 23 | settings, 24 | ) 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /src/commands/invite.ts: -------------------------------------------------------------------------------- 1 | import { send } from '../actions/replyInChannel' 2 | import type { ActionProps } from '../../@types/command' 3 | 4 | export default { 5 | regex(settings: Settings) { 6 | return new RegExp( 7 | `^${settings.prefix}(?:invite)$`, 8 | `gi`, 9 | ) 10 | }, 11 | async action({ msg, settings, match }: ActionProps) { 12 | console.log( 13 | `${ 14 | msg.guild ? msg.guild.name : `Private Message` 15 | } - Invite link (${msg.author.username}) `, 16 | ) 17 | 18 | send( 19 | msg, 20 | `The bot invite link is `, 21 | `none`, 22 | settings, 23 | ) 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /dist/events/receivePrivateMessage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const timein_1 = __importDefault(require("../commands/timein")); 7 | const defaultSettings = require(`../scripts/defaultServerSettings`); 8 | exports.default = async (msg) => { 9 | await timein_1.default.action({ 10 | msg, 11 | settings: defaultSettings, 12 | match: [``, ` `, msg.content], 13 | }); 14 | // msg.channel.send(`I only work in a server channel for now. 15 | // If you're looking for the invite link, it's https://discord.com/api/oauth2/authorize?client_id=723017262369472603&permissions=75840&scope=bot`) 16 | }; 17 | -------------------------------------------------------------------------------- /dist/src/events/receivePrivateMessage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const timein_1 = __importDefault(require("../commands/timein")); 7 | const defaultSettings = require(`../scripts/defaultServerSettings`); 8 | exports.default = async (msg) => { 9 | await timein_1.default.action({ 10 | msg, 11 | settings: defaultSettings, 12 | match: [``, ` `, msg.content], 13 | }); 14 | // msg.channel.send(`I only work in a server channel for now. 15 | // If you're looking for the invite link, it's https://discord.com/api/oauth2/authorize?client_id=723017262369472603&permissions=75840&scope=bot`) 16 | }; 17 | -------------------------------------------------------------------------------- /dist/db/firestore.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firebase_admin_1 = __importDefault(require("firebase-admin")); 7 | /* eslint-disable camelcase */ 8 | firebase_admin_1.default.initializeApp({ 9 | credential: firebase_admin_1.default.credential.cert({ 10 | project_id: process.env.FIREBASE_PROJECT_ID, 11 | client_email: process.env.FIREBASE_CLIENT_EMAIL, 12 | private_key: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, `\n`), 13 | }), 14 | }); 15 | const guild_1 = __importDefault(require("./guild")); 16 | const location_1 = __importDefault(require("./location")); 17 | exports.default = { 18 | ...guild_1.default, 19 | ...location_1.default, 20 | }; 21 | -------------------------------------------------------------------------------- /dist/src/events/addedToServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const { getGuildMembers, } = require(`../scripts/commonFunctions`); 8 | exports.default = async (guild) => { 9 | if (await firestore_1.default.hasGuild({ guildId: guild.id })) 10 | return console.log(`> > > > > > Was re-added to a guild:`, guild.name, guild.id); 11 | await firestore_1.default.addGuild({ 12 | guildId: guild.id, 13 | guildName: guild.name, 14 | }); 15 | console.log(`> > > > > > Was added to a new guild:`, guild.name, guild.id, `(${(await getGuildMembers({ guild })).length} users)`); 16 | }; 17 | -------------------------------------------------------------------------------- /dist/src/db/firestore.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firebase_admin_1 = __importDefault(require("firebase-admin")); 7 | /* eslint-disable camelcase */ 8 | firebase_admin_1.default.initializeApp({ 9 | credential: firebase_admin_1.default.credential.cert({ 10 | project_id: process.env.FIREBASE_PROJECT_ID, 11 | client_email: process.env.FIREBASE_CLIENT_EMAIL, 12 | private_key: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, `\n`), 13 | }), 14 | }); 15 | const guild_1 = __importDefault(require("./guild")); 16 | const location_1 = __importDefault(require("./location")); 17 | exports.default = { 18 | ...guild_1.default, 19 | ...location_1.default, 20 | }; 21 | -------------------------------------------------------------------------------- /dist/events/addedToServer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commonFunctions_1 = require("../scripts/commonFunctions"); 8 | exports.default = async (guild) => { 9 | if (await firestore_1.default.hasGuild({ guildId: guild.id })) 10 | return console.log(`> > > > > > Was re-added to a guild:`, guild.name, guild.id); 11 | await firestore_1.default.addGuild({ 12 | guildId: guild.id, 13 | guildName: guild.name, 14 | }); 15 | console.log(`> > > > > > Was added to a new guild:`, guild.name, guild.id, `(${(await (0, commonFunctions_1.getGuildMembers)({ guild })).length} users)`); 16 | }; 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | require(`dotenv`).config() 2 | import * as Discord from 'discord.js-light' 3 | const manager = new Discord.ShardingManager( 4 | `./dist/bot.js`, 5 | { 6 | token: process.env.DISCORD_TOKEN, 7 | }, 8 | ) 9 | 10 | manager.on(`shardCreate`, (shard: Discord.Shard) => { 11 | console.log(`Launched shard ${shard.id}`) 12 | }) 13 | 14 | console.log(`Launching with shards...`) 15 | 16 | manager 17 | .spawn({ 18 | // amount: shards, 19 | // delay: 5000, 20 | timeout: 100000, 21 | }) 22 | .then((shardCollection) => { 23 | manager 24 | .fetchClientValues(`guilds.cache.size`) 25 | .then((results: any) => { 26 | if (results && Array.isArray(results)) 27 | console.log( 28 | `***** Logged in in ${results.reduce( 29 | (acc: number, guildCount: number) => 30 | acc + guildCount, 31 | 0, 32 | )} total guilds *****`, 33 | ) 34 | }) 35 | .catch(console.error) 36 | }) 37 | -------------------------------------------------------------------------------- /dist/actions/contactGuildAdmin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const commonFunctions_1 = require("../scripts/commonFunctions"); 4 | async function default_1({ guild, message, }) { 5 | if (!guild) 6 | return; 7 | const currentGuildContacts = await (0, commonFunctions_1.getContactsOrOwnerOrModerator)({ 8 | guild, 9 | }); 10 | if (!currentGuildContacts) 11 | return console.log(`Failed to find contact points in server`, guild.name); 12 | currentGuildContacts.forEach((singleContact) => singleContact.user 13 | .send(message.substring(0, 1999)) 14 | .then(() => { 15 | // console.log('Contacted admin', singleContact.user.username) 16 | }) 17 | .catch((err) => { 18 | // console.log( 19 | // `Failed to contact admin ${getLabelFromUser(singleContact)}: ${ 20 | // err.message 21 | // }`, 22 | // ) 23 | })); 24 | } 25 | exports.default = default_1; 26 | -------------------------------------------------------------------------------- /dist/src/actions/contactGuildAdmin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const commonFunctions_1 = require("../scripts/commonFunctions"); 4 | async function default_1({ guild, message, }) { 5 | if (!guild) 6 | return; 7 | const currentGuildContacts = await (0, commonFunctions_1.getContactsOrOwnerOrModerator)({ 8 | guild, 9 | }); 10 | if (!currentGuildContacts) 11 | return console.log(`Failed to find contact points in server`, guild.name); 12 | currentGuildContacts.forEach((singleContact) => singleContact.user 13 | .send(message.substring(0, 1999)) 14 | .then(() => { 15 | // console.log('Contacted admin', singleContact.user.username) 16 | }) 17 | .catch((err) => { 18 | // console.log( 19 | // `Failed to contact admin ${getLabelFromUser(singleContact)}: ${ 20 | // err.message 21 | // }`, 22 | // ) 23 | })); 24 | } 25 | exports.default = default_1; 26 | -------------------------------------------------------------------------------- /src/commands/format.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:format)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.format24 === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle format > ${ 19 | turnOff ? `12-hour` : `24-hour` 20 | } (${msg.author.username}) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | format24: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Times will now be shown in ${ 33 | turnOff ? `12-hour AM/PM format` : `24-hour format` 34 | }.`, 35 | false, 36 | settings, 37 | ) 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /src/commands/toggledeletecommand.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:deletecommands?)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.deleteCommand === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle deletecommand > ${ 19 | turnOff ? `off` : `on` 20 | } (${msg.author.username}) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | deleteCommand: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Bot command messages will ${ 33 | turnOff ? `not ` : `` 34 | }be deleted.`, 35 | false, 36 | settings, 37 | ) 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /src/commands/suppresswarnings.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:suppresswarnings)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.suppressWarnings === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle suppress warnings > ${ 19 | turnOff ? `off` : `on` 20 | } (${msg.author.username}) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | suppressWarnings: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Admin warnings have been ${ 33 | turnOff ? `turned on` : `suppressed` 34 | }.`, 35 | false, 36 | settings, 37 | ) 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /src/commands/toggleautorespond.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:auto[-]?re(spon(d|se|ses)|ply))$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.autoRespond === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle autorespond > ${turnOff ? `off` : `on`} (${ 19 | msg.author.username 20 | }) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | autoRespond: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Auto-responding to @s has been turned ${ 33 | turnOff ? `off` : `on` 34 | }.`, 35 | false, 36 | settings, 37 | ) 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /dist/src/commands/format.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:format)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.format24 === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle format > ${turnOff ? `12-hour` : `24-hour`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | format24: !turnOff, 18 | }, msg.guild.id); 19 | (0, replyInChannel_1.send)(msg, `Times will now be shown in ${turnOff ? `12-hour AM/PM format` : `24-hour format`}.`, false, settings); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/commands/toggleadminonly.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:admins?only)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.adminOnly === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle admin mode > ${turnOff ? `off` : `on`} (${ 19 | msg.author.username 20 | }) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | adminOnly: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Commands ${ 33 | turnOff 34 | ? `may now be used by all users` 35 | : `may now only be used by admins` 36 | }.`, 37 | false, 38 | settings, 39 | ) 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /src/actions/contactGuildAdmin.ts: -------------------------------------------------------------------------------- 1 | import Discord from 'discord.js-light' 2 | import { 3 | getContactsOrOwnerOrModerator, 4 | getLabelFromUser, 5 | } from '../scripts/commonFunctions' 6 | 7 | export default async function ({ 8 | guild, 9 | message, 10 | }: { 11 | guild?: Discord.Guild | null 12 | message: string 13 | }) { 14 | if (!guild) return 15 | 16 | const currentGuildContacts = 17 | await getContactsOrOwnerOrModerator({ 18 | guild, 19 | }) 20 | 21 | if (!currentGuildContacts) 22 | return console.log( 23 | `Failed to find contact points in server`, 24 | guild.name, 25 | ) 26 | currentGuildContacts.forEach((singleContact) => 27 | singleContact.user 28 | .send(message.substring(0, 1999)) 29 | .then(() => { 30 | // console.log('Contacted admin', singleContact.user.username) 31 | }) 32 | .catch((err) => { 33 | // console.log( 34 | // `Failed to contact admin ${getLabelFromUser(singleContact)}: ${ 35 | // err.message 36 | // }`, 37 | // ) 38 | }), 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/commands/removeme.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { getAuthorDisplayName } from '../scripts/commonFunctions' 3 | import { send } from '../actions/replyInChannel' 4 | import type { ActionProps } from '../../@types/command' 5 | 6 | export default { 7 | ignoreAdminOnly: true, 8 | regex(settings: Settings) { 9 | return new RegExp( 10 | `^${settings.prefix}(?:removeme|rm)$`, 11 | `gi`, 12 | ) 13 | }, 14 | async action({ msg, settings }: ActionProps) { 15 | console.log( 16 | `${ 17 | msg.guild?.name 18 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 19 | : `Private Message` 20 | }${msg.guild ? ` (${msg.guild.id})` : ``} - Remove ${ 21 | msg.author.username 22 | }`, 23 | ) 24 | db.removeUserFromGuild({ 25 | guildId: msg.guild?.id, 26 | userId: msg.author.id, 27 | }) 28 | return send( 29 | msg, 30 | `Removed you (${await getAuthorDisplayName( 31 | msg, 32 | )}) from timezone tracking.`, 33 | false, 34 | settings, 35 | ) 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /src/commands/verboseAll.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:verboseall)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | const turnOff = settings.verboseAll === true 15 | console.log( 16 | `${ 17 | msg.guild ? msg.guild.name : `Private Message` 18 | } - Toggle verbose all > ${turnOff ? `off` : `on`} (${ 19 | msg.author.username 20 | }) `, 21 | ) 22 | 23 | await db.setGuildSettings( 24 | { 25 | verboseAll: !turnOff, 26 | }, 27 | msg.guild?.id, 28 | ) 29 | 30 | send( 31 | msg, 32 | `Verbose listings in '${settings.prefix}all' and '${ 33 | settings.prefix 34 | }here' have been ${ 35 | turnOff ? `turned off` : `turned on` 36 | }.`, 37 | false, 38 | settings, 39 | ) 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /dist/src/commands/toggledeletecommand.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:deletecommands?)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.deleteCommand === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle deletecommand > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | guildId: msg.guild.id, 18 | deleteCommand: !turnOff, 19 | }); 20 | (0, replyInChannel_1.send)(msg, `Bot command messages will ${turnOff ? `not ` : ``}be deleted.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/src/commands/suppresswarnings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:suppresswarnings)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.suppressWarnings === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle suppress warnings > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | guildId: msg.guild.id, 18 | suppressWarnings: !turnOff, 19 | }); 20 | (0, replyInChannel_1.send)(msg, `Admin warnings have been ${turnOff ? `turned on` : `suppressed`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/src/commands/toggleautorespond.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:auto[-]?re(spon(d|se|ses)|ply))$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.autoRespond === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle autorespond > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | guildId: msg.guild.id, 18 | autoRespond: !turnOff, 19 | }); 20 | (0, replyInChannel_1.send)(msg, `Auto-responding to @s has been turned ${turnOff ? `off` : `on`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/format.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:format)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.format24 === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle format > ${turnOff ? `12-hour` : `24-hour`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | format24: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Times will now be shown in ${turnOff ? `12-hour AM/PM format` : `24-hour format`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/toggledeletecommand.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:deletecommands?)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.deleteCommand === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle deletecommand > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | deleteCommand: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Bot command messages will ${turnOff ? `not ` : ``}be deleted.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/src/commands/toggleadminonly.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:admins?only)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.adminOnly === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle admin mode > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | guildId: msg.guild.id, 18 | adminOnly: !turnOff, 19 | }); 20 | (0, replyInChannel_1.send)(msg, `Commands ${turnOff 21 | ? `may now be used by all users` 22 | : `may now only be used by admins`}.`, false, settings); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /dist/src/commands/verboseAll.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:verboseall)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | const turnOff = settings.verboseAll === true; 15 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle verbose all > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 16 | await firestore_1.default.setGuildSettings({ 17 | guildId: msg.guild.id, 18 | verboseAll: !turnOff, 19 | }); 20 | (0, replyInChannel_1.send)(msg, `Verbose listings in \'${settings.prefix}all\' and \'${settings.prefix}here\' have been ${turnOff ? `turned off` : `turned on`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/toggleautorespond.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:auto[-]?re(spon(d|se|ses)|ply))$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.autoRespond === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle autorespond > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | autoRespond: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Auto-responding to @s has been turned ${turnOff ? `off` : `on`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/suppresswarnings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:suppresswarnings)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.suppressWarnings === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle suppress warnings > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | suppressWarnings: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Admin warnings have been ${turnOff ? `turned on` : `suppressed`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/verboseAll.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:verboseall)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.verboseAll === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle verbose all > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | verboseAll: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Verbose listings in '${settings.prefix}all' and '${settings.prefix}here' have been ${turnOff ? `turned off` : `turned on`}.`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /dist/commands/toggleadminonly.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:admins?only)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | const turnOff = settings.adminOnly === true; 16 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Toggle admin mode > ${turnOff ? `off` : `on`} (${msg.author.username}) `); 17 | await firestore_1.default.setGuildSettings({ 18 | adminOnly: !turnOff, 19 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 20 | (0, replyInChannel_1.send)(msg, `Commands ${turnOff 21 | ? `may now be used by all users` 22 | : `may now only be used by admins`}.`, false, settings); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timezone-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=16.6.0" 8 | }, 9 | "scripts": { 10 | "gh": "git push heroku master && npm run l", 11 | "hup": "heroku dyno:scale worker=1", 12 | "hdown": "heroku dyno:scale worker=0", 13 | "l": "heroku logs --tail", 14 | "logs": "heroku logs --tail", 15 | "watch": "tsc -w", 16 | "build": "tsc", 17 | "dev": "nodemon ./dist/bot.js", 18 | "start": "node --optimize_for_size --max_old_space_size=460 ./dist" 19 | }, 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "@types/node": "^16.9.1", 24 | "axios": "^0.21.4", 25 | "dayjs": "^1.10.6", 26 | "discord.js-light": "^4.1.4", 27 | "dotenv": "^10.0.0", 28 | "firebase-admin": "^9.11.1", 29 | "fuse.js": "^6.4.6", 30 | "nodemon": "^2.0.12", 31 | "typescript": "^4.4.2" 32 | }, 33 | "devDependencies": { 34 | "@typescript-eslint/eslint-plugin": "^4.31.0", 35 | "@typescript-eslint/parser": "^4.31.0", 36 | "eslint": "^7.32.0", 37 | "eslint-plugin-prettier": "^4.0.0", 38 | "prettier": "^2.4.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dist/src/commands/removeme.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commonFunctions_1 = require("../scripts/commonFunctions"); 8 | const replyInChannel_1 = require("../actions/replyInChannel"); 9 | module.exports = { 10 | ignoreAdminOnly: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:removeme|rm)$`, `gi`); 13 | }, 14 | async action({ msg, settings }) { 15 | console.log(`${msg.guild 16 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 17 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Remove ${msg.author.username}`); 18 | firestore_1.default.removeUserFromGuild({ 19 | guildId: msg.guild.id, 20 | userId: msg.author.id || msg.author.user.id, 21 | }); 22 | return (0, replyInChannel_1.send)(msg, `Removed you (${await (0, commonFunctions_1.getAuthorDisplayName)(msg)}) from timezone tracking.`, false, settings); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /dist/commands/removeme.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commonFunctions_1 = require("../scripts/commonFunctions"); 8 | const replyInChannel_1 = require("../actions/replyInChannel"); 9 | exports.default = { 10 | ignoreAdminOnly: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:removeme|rm)$`, `gi`); 13 | }, 14 | async action({ msg, settings }) { 15 | var _a, _b; 16 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 17 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 18 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Remove ${msg.author.username}`); 19 | firestore_1.default.removeUserFromGuild({ 20 | guildId: (_b = msg.guild) === null || _b === void 0 ? void 0 : _b.id, 21 | userId: msg.author.id, 22 | }); 23 | return (0, replyInChannel_1.send)(msg, `Removed you (${await (0, commonFunctions_1.getAuthorDisplayName)(msg)}) from timezone tracking.`, false, settings); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/db/location.ts: -------------------------------------------------------------------------------- 1 | import memo from '../scripts/memo' 2 | import admin from 'firebase-admin' 3 | 4 | const memoedLocationData = memo(2500) 5 | 6 | let firestore = admin.firestore() 7 | 8 | export default { 9 | async setLocation({ 10 | locationName, 11 | locationSettings, 12 | }: { 13 | locationName: string 14 | locationSettings: LocationData 15 | }) { 16 | const sanitizedName = encodeURIComponent( 17 | locationName.toLowerCase(), 18 | ) 19 | const document = firestore.doc( 20 | `locations/${sanitizedName}`, 21 | ) 22 | await document.set(locationSettings) 23 | memoedLocationData.set(sanitizedName, locationSettings) 24 | console.log( 25 | `Added location ${locationName} to database (${JSON.stringify( 26 | locationSettings, 27 | )})`, 28 | ) 29 | }, 30 | 31 | async getLocation( 32 | locationName: string, 33 | ): Promise { 34 | const sanitizedName = encodeURIComponent( 35 | locationName.toLowerCase(), 36 | ) 37 | const memoed = memoedLocationData.get(sanitizedName) 38 | if (memoed) return memoed 39 | const document = firestore.doc( 40 | `locations/${sanitizedName}`, 41 | ) 42 | const data = ( 43 | await document.get() 44 | ).data() as LocationData 45 | if (!data) return 46 | memoedLocationData.set(sanitizedName, data) 47 | return data 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /dist/db/location.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const memo_1 = __importDefault(require("../scripts/memo")); 7 | const firebase_admin_1 = __importDefault(require("firebase-admin")); 8 | const memoedLocationData = (0, memo_1.default)(2500); 9 | let firestore = firebase_admin_1.default.firestore(); 10 | exports.default = { 11 | async setLocation({ locationName, locationSettings, }) { 12 | const sanitizedName = encodeURIComponent(locationName.toLowerCase()); 13 | const document = firestore.doc(`locations/${sanitizedName}`); 14 | await document.set(locationSettings); 15 | memoedLocationData.set(sanitizedName, locationSettings); 16 | console.log(`Added location ${locationName} to database (${JSON.stringify(locationSettings)})`); 17 | }, 18 | async getLocation(locationName) { 19 | const sanitizedName = encodeURIComponent(locationName.toLowerCase()); 20 | const memoed = memoedLocationData.get(sanitizedName); 21 | if (memoed) 22 | return memoed; 23 | const document = firestore.doc(`locations/${sanitizedName}`); 24 | const data = (await document.get()).data(); 25 | if (!data) 26 | return; 27 | memoedLocationData.set(sanitizedName, data); 28 | return data; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /dist/src/db/location.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const memo_1 = __importDefault(require("../scripts/memo")); 7 | const firebase_admin_1 = __importDefault(require("firebase-admin")); 8 | const memoedLocationData = (0, memo_1.default)(1500); 9 | let firestore = firebase_admin_1.default.firestore(); 10 | exports.default = { 11 | async setLocation({ locationName, locationSettings, }) { 12 | const sanitizedName = encodeURIComponent(locationName.toLowerCase()); 13 | const document = firestore.doc(`locations/${sanitizedName}`); 14 | await document.set(locationSettings); 15 | memoedLocationData.set(sanitizedName, locationSettings); 16 | console.log(`Added location ${locationName} to database (${JSON.stringify(locationSettings)})`); 17 | }, 18 | async getLocation(locationName) { 19 | const sanitizedName = encodeURIComponent(locationName.toLowerCase()); 20 | const memoed = memoedLocationData.get(sanitizedName); 21 | if (memoed) 22 | return memoed; 23 | const document = firestore.doc(`locations/${sanitizedName}`); 24 | const data = (await document.get()).data(); 25 | if (!data) 26 | return; 27 | memoedLocationData.set(sanitizedName, data); 28 | return data; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /dist/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 5 | }) : (function(o, m, k, k2) { 6 | if (k2 === undefined) k2 = k; 7 | o[k2] = m[k]; 8 | })); 9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 10 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 11 | }) : function(o, v) { 12 | o["default"] = v; 13 | }); 14 | var __importStar = (this && this.__importStar) || function (mod) { 15 | if (mod && mod.__esModule) return mod; 16 | var result = {}; 17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 18 | __setModuleDefault(result, mod); 19 | return result; 20 | }; 21 | Object.defineProperty(exports, "__esModule", { value: true }); 22 | require(`dotenv`).config(); 23 | const Discord = __importStar(require("discord.js-light")); 24 | const manager = new Discord.ShardingManager(`./bot.js`, { 25 | token: process.env.DISCORD_TOKEN, 26 | }); 27 | manager.on(`shardCreate`, (shard) => { 28 | console.log(`Launched shard ${shard.id}`); 29 | }); 30 | const shards = 4; 31 | console.log(`Launching with`, shards, `shards`); 32 | manager.spawn({ 33 | amount: shards, 34 | delay: 10000, 35 | timeout: 100000, 36 | }); 37 | -------------------------------------------------------------------------------- /dist/src/commands/timein.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const { currentTimeAt, getLightEmoji, standardizeTimezoneName, } = require(`../scripts/commonFunctions`); 4 | const replyInChannel_1 = require("../actions/replyInChannel"); 5 | const getTimezoneFromLocation = require(`../actions/getTimezoneFromLocation`); 6 | module.exports = { 7 | regex(settings) { 8 | return new RegExp(`^${settings.prefix}(?:timein|ti(?!m))( ?)(.*)$`, `gi`); 9 | }, 10 | async action({ msg, settings, match }) { 11 | console.log(`${msg.guild 12 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 13 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Time for ${match[2]} (${msg.author.username})`); 14 | if (!match[1] || !match[2]) 15 | return (0, replyInChannel_1.send)(msg, `Use this command in the format \`${settings.prefix}timein \` to see the time in a specific location.`, `none`, settings); 16 | // assuming it's a location 17 | const foundTimezone = await getTimezoneFromLocation(match[2]); 18 | if (!foundTimezone) 19 | return (0, replyInChannel_1.send)(msg, `Sorry, I couldn't find a timezone for ${match[2]}.`, false, settings); 20 | (0, replyInChannel_1.send)(msg, `It's ${getLightEmoji(foundTimezone.location)}${currentTimeAt(foundTimezone.location, false, settings.format24)} in ${match[2]}. (${standardizeTimezoneName(foundTimezone.timezoneName)})`, false, settings); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/scripts/timezoneCodeToLocationData.ts: -------------------------------------------------------------------------------- 1 | const timezoneCodes = require(`./timezoneCodesWithPointFives`) 2 | 3 | export default ( 4 | location: string, 5 | ): LocationDataWithUtc | undefined => { 6 | if (!location) return 7 | 8 | location = location // eslint-disable-next-line 9 | .replace(/[<>\[\]()]/gi, ``) // eslint-disable-next-line 10 | .replace(/[_ ]/gi, ` `) 11 | .replace(/@!?\d* */gi, ``) 12 | 13 | // check for UTC command 14 | const UTCMatch = /^(?:utc|gmt) ?(\+|-)? ?(\d*)/gi.exec( 15 | location, 16 | ) 17 | if (UTCMatch) { 18 | const offset = UTCMatch[2] 19 | ? parseInt(UTCMatch[2]) * 20 | (UTCMatch[1] === `-` ? 1 : -1) 21 | : 0 22 | if (offset > 14 || offset < -12) return 23 | 24 | const locationData = { 25 | timezoneName: `UTC${ 26 | offset < 0 ? `+` + offset * -1 : offset * -1 27 | }`, 28 | location: `Etc/GMT${ 29 | offset < 0 ? offset : `+` + offset 30 | }`, 31 | utcOffset: `${offset < 0 ? offset : `+` + offset}`, 32 | } 33 | return locationData 34 | } 35 | 36 | // check for literal timezone code 37 | const timezoneCodeName = location 38 | .replace(/\s*/g, ``) 39 | .toUpperCase() 40 | const foundTimezoneCode = timezoneCodes[timezoneCodeName] 41 | if (foundTimezoneCode !== undefined) { 42 | const locationData = { 43 | timezoneName: timezoneCodeName, 44 | location: `Etc/GMT${ 45 | foundTimezoneCode >= 0 ? `+` : `` 46 | }${foundTimezoneCode}`, 47 | utcOffset: `${ 48 | foundTimezoneCode >= 0 ? `+` : `` 49 | }${foundTimezoneCode}`, 50 | } 51 | 52 | return locationData 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/commands/deleteresponse.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:deleteresponses?) ?(.*)?$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | let seconds = 5 * 60, 15 | turnOff = false 16 | if (match[1]) { 17 | try { 18 | seconds = parseInt(match[1]) 19 | if (seconds === 0) seconds = 1 20 | } catch (e) { 21 | return send( 22 | msg, 23 | `Use this command in the format \`${settings.prefix}deleteresponse \` to auto-delete responses. Repeat the command with no number to turn deletion off.`, 24 | `none`, 25 | settings, 26 | ) 27 | } 28 | } else { 29 | turnOff = settings.deleteResponse !== false 30 | } 31 | 32 | console.log( 33 | `${ 34 | msg.guild ? msg.guild.name : `Private Message` 35 | } - Set deleteResponse > ${ 36 | turnOff ? `off` : seconds 37 | } (${msg.author.username}) `, 38 | ) 39 | 40 | await db.setGuildSettings( 41 | { 42 | deleteResponse: turnOff ? false : seconds, 43 | }, 44 | msg.guild?.id, 45 | ) 46 | 47 | send( 48 | msg, 49 | `Bot response messages will ${ 50 | turnOff 51 | ? `not be deleted.` 52 | : `be deleted after ${seconds} seconds.` 53 | }`, 54 | false, 55 | settings, 56 | ) 57 | }, 58 | } 59 | -------------------------------------------------------------------------------- /dist/src/scripts/timezoneCodeToLocationData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const timezoneCodes = require(`./timezoneCodesWithPointFives`); 4 | exports.default = (location) => { 5 | if (!location) 6 | return; 7 | location = location // eslint-disable-next-line 8 | .replace(/[<>\[\]()]/gi, ``) // eslint-disable-next-line 9 | .replace(/[_ ]/gi, ` `) 10 | .replace(/@!?\d* */gi, ``); 11 | // check for UTC command 12 | const UTCMatch = /^(?:utc|gmt) ?(\+|-)? ?(\d*)/gi.exec(location); 13 | if (UTCMatch) { 14 | const offset = UTCMatch[2] 15 | ? parseInt(UTCMatch[2]) * 16 | (UTCMatch[1] === `-` ? 1 : -1) 17 | : 0; 18 | if (offset > 14 || offset < -12) 19 | return; 20 | const locationData = { 21 | timezoneName: `UTC${offset < 0 ? offset : `+` + offset}`, 22 | location: `Etc/GMT${offset < 0 ? offset : `+` + offset}`, 23 | utcOffset: `${offset < 0 ? offset : `+` + offset}`, 24 | }; 25 | return locationData; 26 | } 27 | // check for literal timezone code 28 | const timezoneCodeName = location 29 | .replace(/\s*/g, ``) 30 | .toUpperCase(); 31 | const foundTimezoneCode = timezoneCodes[timezoneCodeName]; 32 | if (foundTimezoneCode !== undefined) { 33 | const locationData = { 34 | timezoneName: timezoneCodeName, 35 | location: `Etc/GMT${foundTimezoneCode >= 0 ? `+` : ``}${foundTimezoneCode}`, 36 | utcOffset: `${foundTimezoneCode >= 0 ? `+` : ``}${foundTimezoneCode}`, 37 | }; 38 | return locationData; 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /dist/scripts/timezoneCodeToLocationData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const timezoneCodes = require(`./timezoneCodesWithPointFives`); 4 | exports.default = (location) => { 5 | if (!location) 6 | return; 7 | location = location // eslint-disable-next-line 8 | .replace(/[<>\[\]()]/gi, ``) // eslint-disable-next-line 9 | .replace(/[_ ]/gi, ` `) 10 | .replace(/@!?\d* */gi, ``); 11 | // check for UTC command 12 | const UTCMatch = /^(?:utc|gmt) ?(\+|-)? ?(\d*)/gi.exec(location); 13 | if (UTCMatch) { 14 | const offset = UTCMatch[2] 15 | ? parseInt(UTCMatch[2]) * 16 | (UTCMatch[1] === `-` ? 1 : -1) 17 | : 0; 18 | if (offset > 14 || offset < -12) 19 | return; 20 | const locationData = { 21 | timezoneName: `UTC${offset < 0 ? `+` + offset * -1 : offset * -1}`, 22 | location: `Etc/GMT${offset < 0 ? offset : `+` + offset}`, 23 | utcOffset: `${offset < 0 ? offset : `+` + offset}`, 24 | }; 25 | return locationData; 26 | } 27 | // check for literal timezone code 28 | const timezoneCodeName = location 29 | .replace(/\s*/g, ``) 30 | .toUpperCase(); 31 | const foundTimezoneCode = timezoneCodes[timezoneCodeName]; 32 | if (foundTimezoneCode !== undefined) { 33 | const locationData = { 34 | timezoneName: timezoneCodeName, 35 | location: `Etc/GMT${foundTimezoneCode >= 0 ? `+` : ``}${foundTimezoneCode}`, 36 | utcOffset: `${foundTimezoneCode >= 0 ? `+` : ``}${foundTimezoneCode}`, 37 | }; 38 | return locationData; 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /dist/src/commands/me.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const { getLightEmoji, standardizeTimezoneName, currentTimeAt, } = require(`../scripts/commonFunctions`); 8 | const replyInChannel_1 = require("../actions/replyInChannel"); 9 | module.exports = { 10 | ignoreAdminOnly: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:me|m)$`, `gi`); 13 | }, 14 | async action({ msg, settings, senderIsAdmin, }) { 15 | console.log(`${msg.guild 16 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 17 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Me (${msg.author.username})`); 18 | const foundUser = await firestore_1.default.getUserInGuildFromId({ 19 | guildId: msg.guild.id, 20 | userId: msg.author.id, 21 | }); 22 | if (!foundUser) { 23 | if (settings.adminOnly && !senderIsAdmin) 24 | return (0, replyInChannel_1.send)(msg, `There's no timezone set for you.`, false, settings); 25 | return (0, replyInChannel_1.send)(msg, `You haven't set a timezone for yourself yet! Use "${settings.prefix}set " to set your timezone.`, false, settings); 26 | } 27 | return (0, replyInChannel_1.send)(msg, `Your timezone is set to ${standardizeTimezoneName(foundUser.timezoneName)}. (${getLightEmoji(foundUser.location)}${currentTimeAt(foundUser.location, false, settings.format24)})`, false, settings); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /dist/scripts/memo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function default_1(limit, name) { 4 | return { 5 | memos: {}, 6 | limit, 7 | set: function (key, value) { 8 | this.memos[key] = { value, memoAddedTime: Date.now() }; 9 | if (!this.limit || 10 | Object.keys(this.memos).length <= this.limit) 11 | return; 12 | const oldest = Object.keys(this.memos).reduce((oldest, current) => this.memos[current].memoAddedTime < 13 | oldest.memoAddedTime 14 | ? { 15 | memoAddedTime: this.memos[current].memoAddedTime, 16 | key: current, 17 | } 18 | : oldest, { memoAddedTime: Date.now() }); 19 | if (oldest.key) 20 | delete this.memos[oldest.key]; 21 | }, 22 | get: function (key) { 23 | const found = this.memos[key] 24 | ? this.memos[key].value 25 | : undefined; 26 | if (found) 27 | delete found.memoAddedTime; 28 | return found; 29 | }, 30 | updateProp: function (key, prop, newData) { 31 | const found = this.memos[key] 32 | ? this.memos[key].value 33 | : undefined; 34 | if (found && typeof found === `object`) 35 | found[prop] = newData; 36 | return found; 37 | }, 38 | delete: function (key) { 39 | delete this.memos[key]; 40 | }, 41 | all: function () { 42 | return Object.values(this.memos).map((v) => v.value); 43 | }, 44 | }; 45 | } 46 | exports.default = default_1; 47 | -------------------------------------------------------------------------------- /dist/src/commands/role.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const all = require(`./all`); 4 | const replyInChannel_1 = require("../actions/replyInChannel"); 5 | const { getGuildMembers, } = require(`../scripts/commonFunctions`); 6 | module.exports = { 7 | regex(settings) { 8 | return new RegExp(`^${settings.prefix}(?:role|r)( )(.*)$`, `gi`); 9 | }, 10 | async action({ msg, match, settings }) { 11 | let roleId = match[2]; 12 | if (roleId.indexOf(`<@&`) === 0) 13 | roleId = roleId.substring(3, roleId.length - 1); 14 | console.log(`${msg.guild 15 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 16 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Role (${roleId})`); 17 | const roles = (await msg.guild.roles.fetch()).cache.array(); 18 | const role = roles.find((r) => r.id === roleId || r.name === roleId); 19 | if (!role) 20 | return (0, replyInChannel_1.send)(msg, `The 'role' command lists the time for everyone in a certain role. I couldn't find a role by the name you entered.`, false, settings); 21 | // this is just to prime the cache — if we don't, the cache doesn't necessarily have all users in it when we check for role members. 22 | await getGuildMembers({ msg }); 23 | const members = await role.members.array(); 24 | if (!members.length) 25 | return (0, replyInChannel_1.send)(msg, `I couldn't find any members in that role.`, false, settings); 26 | all.action({ 27 | msg, 28 | settings, 29 | match, 30 | users: members, 31 | prependText: `in \`@${role.name}\``, 32 | }); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /dist/src/scripts/memo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function default_1(limit, name) { 4 | return { 5 | memos: {}, 6 | limit, 7 | set: function (key, value) { 8 | this.memos[key] = { value, memoAddedTime: Date.now() }; 9 | if (!this.limit || 10 | Object.keys(this.memos).length <= this.limit) 11 | return; 12 | const oldest = Object.keys(this.memos).reduce((oldest, current) => this.memos[current].memoAddedTime < 13 | oldest.memoAddedTime 14 | ? { 15 | memoAddedTime: this.memos[current].memoAddedTime, 16 | key: current, 17 | } 18 | : oldest, { memoAddedTime: Date.now() }); 19 | if (oldest.key) 20 | delete this.memos[oldest.key]; 21 | }, 22 | get: function (key) { 23 | const found = this.memos[key] 24 | ? this.memos[key].value 25 | : undefined; 26 | if (found) 27 | delete found.memoAddedTime; 28 | return found; 29 | }, 30 | updateProp: function (key, prop, newData) { 31 | const found = this.memos[key] 32 | ? this.memos[key].value 33 | : undefined; 34 | if (found && typeof found === `object`) 35 | found[prop] = newData; 36 | return found; 37 | }, 38 | delete: function (key) { 39 | delete this.memos[key]; 40 | }, 41 | all: function () { 42 | return Object.values(this.memos).map((v) => v.value); 43 | }, 44 | }; 45 | } 46 | exports.default = default_1; 47 | -------------------------------------------------------------------------------- /dist/src/commands/deleteresponse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:deleteresponses?) ?(.*)?$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | let seconds = 5 * 60, turnOff = false; 15 | if (match[1]) { 16 | try { 17 | seconds = parseInt(match[1]); 18 | if (seconds === 0) 19 | seconds = 1; 20 | } 21 | catch (e) { 22 | return (0, replyInChannel_1.send)(msg, `Use this command in the format \`${settings.prefix}deleteresponse \` to auto-delete responses. Repeat the command with no number to turn deletion off.`, `none`, settings); 23 | } 24 | } 25 | else { 26 | turnOff = settings.deleteResponse !== false; 27 | } 28 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Set deleteResponse > ${turnOff ? `off` : seconds} (${msg.author.username}) `); 29 | await firestore_1.default.setGuildSettings({ 30 | deleteResponse: turnOff ? false : seconds, 31 | }, msg.guild.id); 32 | (0, replyInChannel_1.send)(msg, `Bot response messages will ${turnOff 33 | ? `not be deleted.` 34 | : `be deleted after ${seconds} seconds.`}`, false, settings); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /src/commands/me.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { 3 | getLightEmoji, 4 | standardizeTimezoneName, 5 | currentTimeAt, 6 | } from '../scripts/commonFunctions' 7 | import { send } from '../actions/replyInChannel' 8 | import type { ActionProps } from '../../@types/command' 9 | 10 | export default { 11 | ignoreAdminOnly: true, 12 | regex(settings: Settings) { 13 | return new RegExp(`^${settings.prefix}(?:me|m)$`, `gi`) 14 | }, 15 | async action({ 16 | msg, 17 | settings, 18 | senderIsAdmin, 19 | }: ActionProps) { 20 | console.log( 21 | `${ 22 | msg.guild?.name 23 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 24 | : `Private Message` 25 | }${msg.guild ? ` (${msg.guild.id})` : ``} - Me (${ 26 | msg.author.username 27 | })`, 28 | ) 29 | const foundUser = await db.getUserInGuildFromId({ 30 | guildId: msg.guild?.id, 31 | userId: msg.author.id, 32 | }) 33 | if (!foundUser) { 34 | if (settings.adminOnly && !senderIsAdmin) 35 | return send( 36 | msg, 37 | `There's no timezone set for you.`, 38 | false, 39 | settings, 40 | ) 41 | return send( 42 | msg, 43 | `You haven't set a timezone for yourself yet! Use "${settings.prefix}set " to set your timezone.`, 44 | false, 45 | settings, 46 | ) 47 | } 48 | return send( 49 | msg, 50 | `Your timezone is set to ${standardizeTimezoneName( 51 | foundUser.timezoneName, 52 | )}. (${getLightEmoji( 53 | foundUser.location, 54 | )}${currentTimeAt( 55 | foundUser.location, 56 | false, 57 | Boolean(settings.format24), 58 | )})`, 59 | false, 60 | settings, 61 | ) 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /dist/commands/timein.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const commonFunctions_1 = require("../scripts/commonFunctions"); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const getTimezoneFromLocation_1 = __importDefault(require("../actions/getTimezoneFromLocation")); 9 | exports.default = { 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:timein|ti(?!m))( ?)(.*)$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 16 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 17 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Time in ${match[2]} (${msg.author.username})`); 18 | if (!match[1] || !match[2]) 19 | return (0, replyInChannel_1.send)(msg, `Use this command in the format \`${settings.prefix}timein \` to see the time in a specific location.`, `none`, settings); 20 | // assuming it's a location 21 | const foundTimezone = await (0, getTimezoneFromLocation_1.default)(match[2]); 22 | if (!foundTimezone) 23 | return (0, replyInChannel_1.send)(msg, `Sorry, I couldn't find a timezone for ${match[2]}.`, false, settings); 24 | (0, replyInChannel_1.send)(msg, `It's ${(0, commonFunctions_1.getLightEmoji)(foundTimezone.location)}${(0, commonFunctions_1.currentTimeAt)(foundTimezone.location, false, Boolean(settings.format24))} in ${match[2]}. (${(0, commonFunctions_1.standardizeTimezoneName)(foundTimezone.timezoneName)})`, false, settings); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /dist/commands/deleteresponse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:deleteresponses?) ?(.*)?$`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a; 15 | let seconds = 5 * 60, turnOff = false; 16 | if (match[1]) { 17 | try { 18 | seconds = parseInt(match[1]); 19 | if (seconds === 0) 20 | seconds = 1; 21 | } 22 | catch (e) { 23 | return (0, replyInChannel_1.send)(msg, `Use this command in the format \`${settings.prefix}deleteresponse \` to auto-delete responses. Repeat the command with no number to turn deletion off.`, `none`, settings); 24 | } 25 | } 26 | else { 27 | turnOff = settings.deleteResponse !== false; 28 | } 29 | console.log(`${msg.guild ? msg.guild.name : `Private Message`} - Set deleteResponse > ${turnOff ? `off` : seconds} (${msg.author.username}) `); 30 | await firestore_1.default.setGuildSettings({ 31 | deleteResponse: turnOff ? false : seconds, 32 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 33 | (0, replyInChannel_1.send)(msg, `Bot response messages will ${turnOff 34 | ? `not be deleted.` 35 | : `be deleted after ${seconds} seconds.`}`, false, settings); 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 5 | }) : (function(o, m, k, k2) { 6 | if (k2 === undefined) k2 = k; 7 | o[k2] = m[k]; 8 | })); 9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 10 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 11 | }) : function(o, v) { 12 | o["default"] = v; 13 | }); 14 | var __importStar = (this && this.__importStar) || function (mod) { 15 | if (mod && mod.__esModule) return mod; 16 | var result = {}; 17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 18 | __setModuleDefault(result, mod); 19 | return result; 20 | }; 21 | Object.defineProperty(exports, "__esModule", { value: true }); 22 | require(`dotenv`).config(); 23 | const Discord = __importStar(require("discord.js-light")); 24 | const manager = new Discord.ShardingManager(`./dist/bot.js`, { 25 | token: process.env.DISCORD_TOKEN, 26 | }); 27 | manager.on(`shardCreate`, (shard) => { 28 | console.log(`Launched shard ${shard.id}`); 29 | }); 30 | console.log(`Launching with shards...`); 31 | manager 32 | .spawn({ 33 | // amount: shards, 34 | // delay: 5000, 35 | timeout: 100000, 36 | }) 37 | .then((shardCollection) => { 38 | manager 39 | .fetchClientValues(`guilds.cache.size`) 40 | .then((results) => { 41 | if (results && Array.isArray(results)) 42 | console.log(`***** Logged in in ${results.reduce((acc, guildCount) => acc + guildCount, 0)} total guilds *****`); 43 | }) 44 | .catch(console.error); 45 | }); 46 | -------------------------------------------------------------------------------- /src/commands/timein.ts: -------------------------------------------------------------------------------- 1 | import { 2 | currentTimeAt, 3 | getLightEmoji, 4 | standardizeTimezoneName, 5 | } from '../scripts/commonFunctions' 6 | import { send } from '../actions/replyInChannel' 7 | import type { ActionProps } from '../../@types/command' 8 | import getTimezoneFromLocation from '../actions/getTimezoneFromLocation' 9 | 10 | export default { 11 | regex(settings: Settings) { 12 | return new RegExp( 13 | `^${settings.prefix}(?:timein|ti(?!m))( ?)(.*)$`, 14 | `gi`, 15 | ) 16 | }, 17 | async action({ msg, settings, match }: ActionProps) { 18 | console.log( 19 | `${ 20 | msg.guild?.name 21 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 22 | : `Private Message` 23 | }${msg.guild ? ` (${msg.guild.id})` : ``} - Time in ${ 24 | match[2] 25 | } (${msg.author.username})`, 26 | ) 27 | 28 | if (!match[1] || !match[2]) 29 | return send( 30 | msg, 31 | `Use this command in the format \`${settings.prefix}timein \` to see the time in a specific location.`, 32 | `none`, 33 | settings, 34 | ) 35 | 36 | // assuming it's a location 37 | const foundTimezone = await getTimezoneFromLocation( 38 | match[2], 39 | ) 40 | if (!foundTimezone) 41 | return send( 42 | msg, 43 | `Sorry, I couldn't find a timezone for ${match[2]}.`, 44 | false, 45 | settings, 46 | ) 47 | 48 | send( 49 | msg, 50 | `It's ${getLightEmoji( 51 | foundTimezone.location, 52 | )}${currentTimeAt( 53 | foundTimezone.location, 54 | false, 55 | Boolean(settings.format24), 56 | )} in ${match[2]}. (${standardizeTimezoneName( 57 | foundTimezone.timezoneName, 58 | )})`, 59 | false, 60 | settings, 61 | ) 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /src/scripts/memo.ts: -------------------------------------------------------------------------------- 1 | export default function ( 2 | limit: number, 3 | name?: string, 4 | ): { 5 | memos: { [key: string]: any } 6 | limit: number 7 | set: (key: string, value: any) => void 8 | get: (key: string) => any 9 | updateProp: ( 10 | key: string, 11 | prop: string, 12 | newData: any, 13 | ) => void 14 | delete: (key: string) => void 15 | all: () => any[] 16 | } { 17 | return { 18 | memos: {}, 19 | limit, 20 | set: function (key: string, value: any) { 21 | this.memos[key] = { value, memoAddedTime: Date.now() } 22 | if ( 23 | !this.limit || 24 | Object.keys(this.memos).length <= this.limit 25 | ) 26 | return 27 | const oldest: any = Object.keys(this.memos).reduce( 28 | (oldest, current) => 29 | this.memos[current].memoAddedTime < 30 | oldest.memoAddedTime 31 | ? { 32 | memoAddedTime: 33 | this.memos[current].memoAddedTime, 34 | key: current, 35 | } 36 | : oldest, 37 | { memoAddedTime: Date.now() }, 38 | ) 39 | if (oldest.key) delete this.memos[oldest.key] 40 | }, 41 | get: function (key) { 42 | const found = this.memos[key] 43 | ? this.memos[key].value 44 | : undefined 45 | if (found) delete found.memoAddedTime 46 | return found 47 | }, 48 | updateProp: function (key: string, prop, newData) { 49 | const found = this.memos[key] 50 | ? this.memos[key].value 51 | : undefined 52 | if (found && typeof found === `object`) 53 | found[prop] = newData 54 | return found 55 | }, 56 | delete: function (key) { 57 | delete this.memos[key] 58 | }, 59 | all: function () { 60 | return Object.values(this.memos).map((v) => v.value) 61 | }, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /dist/commands/me.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const commonFunctions_1 = require("../scripts/commonFunctions"); 8 | const replyInChannel_1 = require("../actions/replyInChannel"); 9 | exports.default = { 10 | ignoreAdminOnly: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:me|m)$`, `gi`); 13 | }, 14 | async action({ msg, settings, senderIsAdmin, }) { 15 | var _a, _b; 16 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 17 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 18 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Me (${msg.author.username})`); 19 | const foundUser = await firestore_1.default.getUserInGuildFromId({ 20 | guildId: (_b = msg.guild) === null || _b === void 0 ? void 0 : _b.id, 21 | userId: msg.author.id, 22 | }); 23 | if (!foundUser) { 24 | if (settings.adminOnly && !senderIsAdmin) 25 | return (0, replyInChannel_1.send)(msg, `There's no timezone set for you.`, false, settings); 26 | return (0, replyInChannel_1.send)(msg, `You haven't set a timezone for yourself yet! Use "${settings.prefix}set " to set your timezone.`, false, settings); 27 | } 28 | return (0, replyInChannel_1.send)(msg, `Your timezone is set to ${(0, commonFunctions_1.standardizeTimezoneName)(foundUser.timezoneName)}. (${(0, commonFunctions_1.getLightEmoji)(foundUser.location)}${(0, commonFunctions_1.currentTimeAt)(foundUser.location, false, Boolean(settings.format24))})`, false, settings); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /dist/src/commands/removeuser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const { getLabelFromUser, } = require(`../scripts/commonFunctions`); 9 | module.exports = { 10 | admin: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:removeuser|ru) (.*)`, `gi`); 13 | }, 14 | expectsUserInRegexSlot: 1, 15 | async action({ msg, match, typedUser, settings, }) { 16 | console.log(`${msg.guild 17 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 18 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Admin remove user ${match[1]} (${msg.author.username})`); 19 | if (!match[1]) { 20 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}removeuser to remove that user's timezone.`, false, settings); 21 | } 22 | if (!typedUser) { 23 | return (0, replyInChannel_1.send)(msg, `I couldn't find a user by the name ${match[1]}.`, false, settings); 24 | } 25 | const success = await firestore_1.default.removeUserFromGuild({ 26 | guildId: msg.guild.id, 27 | userId: typedUser.id || typedUser.user.id, 28 | }); 29 | if (success === true) 30 | return (0, replyInChannel_1.send)(msg, `Removed ${await getLabelFromUser(typedUser)} from timezone tracking.`, false, settings); 31 | else if (success) 32 | return (0, replyInChannel_1.send)(msg, success, false, settings); 33 | return (0, replyInChannel_1.send)(msg, `An unknown error occurred.`, false, settings); 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/commands/removeuser.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | import { getLabelFromUser } from '../scripts/commonFunctions' 5 | 6 | export default { 7 | admin: true, 8 | regex(settings: Settings) { 9 | return new RegExp( 10 | `^${settings.prefix}(?:removeuser|ru) (.*)`, 11 | `gi`, 12 | ) 13 | }, 14 | expectsUserInRegexSlot: 1, 15 | async action({ 16 | msg, 17 | match, 18 | typedUser, 19 | settings, 20 | }: ActionProps) { 21 | console.log( 22 | `${ 23 | msg.guild?.name 24 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 25 | : `Private Message` 26 | }${ 27 | msg.guild ? ` (${msg.guild.id})` : `` 28 | } - Admin remove user ${match[1]} (${ 29 | msg.author.username 30 | })`, 31 | ) 32 | if (!match[1]) { 33 | return send( 34 | msg, 35 | `Use this command in the format ${settings.prefix}removeuser to remove that user's timezone.`, 36 | false, 37 | settings, 38 | ) 39 | } 40 | if (!typedUser) { 41 | return send( 42 | msg, 43 | `I couldn't find a user by the name ${match[1]}.`, 44 | false, 45 | settings, 46 | ) 47 | } 48 | const success = await db.removeUserFromGuild({ 49 | guildId: msg.guild?.id, 50 | userId: typedUser.id || typedUser.user.id, 51 | }) 52 | if (success === true) 53 | return send( 54 | msg, 55 | `Removed ${await getLabelFromUser( 56 | typedUser, 57 | )} from timezone tracking.`, 58 | false, 59 | settings, 60 | ) 61 | else if (success) 62 | return send(msg, success, false, settings) 63 | return send( 64 | msg, 65 | `An unknown error occurred.`, 66 | false, 67 | settings, 68 | ) 69 | }, 70 | } 71 | -------------------------------------------------------------------------------- /src/commands/role.ts: -------------------------------------------------------------------------------- 1 | import all from './all' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | import { getGuildMembers } from '../scripts/commonFunctions' 5 | 6 | export default { 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^${settings.prefix}(?:role)( )?(.*)$`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, match, settings }: ActionProps) { 14 | let roleId = match[2] 15 | if (roleId.indexOf(`<@&`) === 0) 16 | roleId = roleId.substring(3, roleId.length - 1) 17 | 18 | console.log( 19 | `${ 20 | msg.guild?.name 21 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 22 | : `Private Message` 23 | }${ 24 | msg.guild ? ` (${msg.guild.id})` : `` 25 | } - Role (${roleId})`, 26 | ) 27 | const roles = [ 28 | ...((await msg.guild?.roles.fetch()) || []).values(), 29 | ] 30 | const role = roles.find( 31 | (r) => r.id === roleId || r.name === roleId, 32 | ) 33 | if (!role) 34 | return send( 35 | msg, 36 | `The 'role' command lists the time for everyone in a certain role. I couldn't find a role by the name you entered. Use \`${settings.prefix}role <@role or role name>\` to run this command.`, 37 | `none`, 38 | settings, 39 | ) 40 | 41 | // this is just to prime the cache — if we don't, the cache doesn't necessarily have all users in it when we check for role members. 42 | await getGuildMembers({ msg }) 43 | 44 | const members = [...(await role.members.values())] 45 | if (!members.length) 46 | return send( 47 | msg, 48 | `I couldn't find any members in that role.`, 49 | false, 50 | settings, 51 | ) 52 | 53 | all.action({ 54 | msg, 55 | settings, 56 | match, 57 | users: members, 58 | prependText: `in \`@${role.name}\``, 59 | }) 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /src/commands/setRepeatAnnounceTime.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | import defaultServerSettings from '../scripts/defaultServerSettings' 5 | 6 | export default { 7 | admin: true, 8 | regex(settings: Settings) { 9 | return new RegExp( 10 | `^${settings.prefix}(?:repeatannouncetime|rat)( )?(.*)?$`, 11 | `gi`, 12 | ) 13 | }, 14 | async action({ msg, settings, match }: ActionProps) { 15 | const currentRepeatAnnounceTime = 16 | settings.repeatAnnounceTime || 17 | defaultServerSettings.repeatAnnounceTime 18 | let newTime: number 19 | try { 20 | newTime = parseInt(match[2] || `0`) 21 | } catch (e) { 22 | newTime = 0 23 | return send( 24 | msg, 25 | `Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change the minimum time span for announcing the same user's timezone.`, 26 | false, 27 | settings, 28 | ) 29 | } 30 | if (isNaN(newTime) || !newTime || newTime === 0) { 31 | return send( 32 | msg, 33 | `The current minimum time span for announcing the same user's timezone is ${currentRepeatAnnounceTime} minutes. Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change it.`, 34 | false, 35 | settings, 36 | ) 37 | } 38 | 39 | console.log( 40 | `${msg.guild ? msg.guild.name : `Private Message`}${ 41 | msg.guild ? ` (${msg.guild.id})` : `` 42 | } - Set repeat announce time > ${newTime} (${ 43 | msg.author.username 44 | }) `, 45 | ) 46 | 47 | await db.setGuildSettings( 48 | { 49 | repeatAnnounceTime: newTime, 50 | }, 51 | msg.guild?.id, 52 | ) 53 | 54 | send( 55 | msg, 56 | `The minimum time span for announcing the same user's timezone has been set to ${newTime} minutes.`, 57 | false, 58 | settings, 59 | ) 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /dist/commands/removeuser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const commonFunctions_1 = require("../scripts/commonFunctions"); 9 | exports.default = { 10 | admin: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:removeuser|ru) (.*)`, `gi`); 13 | }, 14 | expectsUserInRegexSlot: 1, 15 | async action({ msg, match, typedUser, settings, }) { 16 | var _a, _b; 17 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 18 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 19 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Admin remove user ${match[1]} (${msg.author.username})`); 20 | if (!match[1]) { 21 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}removeuser to remove that user's timezone.`, false, settings); 22 | } 23 | if (!typedUser) { 24 | return (0, replyInChannel_1.send)(msg, `I couldn't find a user by the name ${match[1]}.`, false, settings); 25 | } 26 | const success = await firestore_1.default.removeUserFromGuild({ 27 | guildId: (_b = msg.guild) === null || _b === void 0 ? void 0 : _b.id, 28 | userId: typedUser.id || typedUser.user.id, 29 | }); 30 | if (success === true) 31 | return (0, replyInChannel_1.send)(msg, `Removed ${await (0, commonFunctions_1.getLabelFromUser)(typedUser)} from timezone tracking.`, false, settings); 32 | else if (success) 33 | return (0, replyInChannel_1.send)(msg, success, false, settings); 34 | return (0, replyInChannel_1.send)(msg, `An unknown error occurred.`, false, settings); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /dist/commands/setRepeatAnnounceTime.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const defaultServerSettings_1 = __importDefault(require("../scripts/defaultServerSettings")); 9 | exports.default = { 10 | admin: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:repeatannouncetime|rat)( )?(.*)?$`, `gi`); 13 | }, 14 | async action({ msg, settings, match }) { 15 | var _a; 16 | const currentRepeatAnnounceTime = settings.repeatAnnounceTime || 17 | defaultServerSettings_1.default.repeatAnnounceTime; 18 | let newTime; 19 | try { 20 | newTime = parseInt(match[2] || `0`); 21 | } 22 | catch (e) { 23 | newTime = 0; 24 | return (0, replyInChannel_1.send)(msg, `Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change the minimum time span for announcing the same user's timezone.`, false, settings); 25 | } 26 | if (isNaN(newTime) || !newTime || newTime === 0) { 27 | return (0, replyInChannel_1.send)(msg, `The current minimum time span for announcing the same user's timezone is ${currentRepeatAnnounceTime} minutes. Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change it.`, false, settings); 28 | } 29 | console.log(`${msg.guild ? msg.guild.name : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Set repeat announce time > ${newTime} (${msg.author.username}) `); 30 | await firestore_1.default.setGuildSettings({ 31 | repeatAnnounceTime: newTime, 32 | }, (_a = msg.guild) === null || _a === void 0 ? void 0 : _a.id); 33 | (0, replyInChannel_1.send)(msg, `The minimum time span for announcing the same user's timezone has been set to ${newTime} minutes.`, false, settings); 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /dist/commands/role.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const all_1 = __importDefault(require("./all")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const commonFunctions_1 = require("../scripts/commonFunctions"); 9 | exports.default = { 10 | regex(settings) { 11 | return new RegExp(`^${settings.prefix}(?:role)( )?(.*)$`, `gi`); 12 | }, 13 | async action({ msg, match, settings }) { 14 | var _a, _b; 15 | let roleId = match[2]; 16 | if (roleId.indexOf(`<@&`) === 0) 17 | roleId = roleId.substring(3, roleId.length - 1); 18 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 19 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 20 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Role (${roleId})`); 21 | const roles = [ 22 | ...((await ((_b = msg.guild) === null || _b === void 0 ? void 0 : _b.roles.fetch())) || []).values(), 23 | ]; 24 | const role = roles.find((r) => r.id === roleId || r.name === roleId); 25 | if (!role) 26 | return (0, replyInChannel_1.send)(msg, `The 'role' command lists the time for everyone in a certain role. I couldn't find a role by the name you entered. Use \`${settings.prefix}role <@role or role name>\` to run this command.`, `none`, settings); 27 | // this is just to prime the cache — if we don't, the cache doesn't necessarily have all users in it when we check for role members. 28 | await (0, commonFunctions_1.getGuildMembers)({ msg }); 29 | const members = [...(await role.members.values())]; 30 | if (!members.length) 31 | return (0, replyInChannel_1.send)(msg, `I couldn't find any members in that role.`, false, settings); 32 | all_1.default.action({ 33 | msg, 34 | settings, 35 | match, 36 | users: members, 37 | prependText: `in \`@${role.name}\``, 38 | }); 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /dist/src/commands/setRepeatAnnounceTime.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const defaultServerSettings = require(`../scripts/defaultServerSettings`); 9 | module.exports = { 10 | admin: true, 11 | regex(settings) { 12 | return new RegExp(`^${settings.prefix}(?:repeatannouncetime|rat)( )?(.*)?$`, `gi`); 13 | }, 14 | async action({ msg, settings, match }) { 15 | const currentRepeatAnnounceTime = settings.repeatAnnounceTime || 16 | defaultServerSettings.repeatAnnounceTime; 17 | let newTime = match[2]; 18 | if (!newTime && newTime !== 0 && newTime !== `0`) { 19 | return (0, replyInChannel_1.send)(msg, `The current minimum time span for announcing the same user's timezone is ${currentRepeatAnnounceTime} minutes. Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change it.`, false, settings); 20 | } 21 | console.log(`${msg.guild ? msg.guild.name : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Set repeat announce time > ${newTime} (${msg.author.username}) `); 22 | try { 23 | newTime = parseInt(newTime); 24 | } 25 | catch (e) { 26 | return (0, replyInChannel_1.send)(msg, `Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change the minimum time span for announcing the same user's timezone.`, false, settings); 27 | } 28 | if (isNaN(newTime)) 29 | return (0, replyInChannel_1.send)(msg, `Use \`${settings.prefix}repeatannouncetime <# of minutes>\` to change the minimum time span for announcing the same user's timezone.`, false, settings); 30 | await firestore_1.default.setGuildSettings({ 31 | guildId: msg.guild.id, 32 | repeatAnnounceTime: newTime, 33 | }); 34 | (0, replyInChannel_1.send)(msg, `The minimum time span for announcing the same user's timezone has been set to ${newTime} minutes.`, false, settings); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /dist/src/commands/setuser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const { getLabelFromUser, getLightEmoji, currentTimeAt, } = require(`../scripts/commonFunctions`); 9 | const getTimezoneFromLocation = require(`../actions/getTimezoneFromLocation`); 10 | module.exports = { 11 | admin: true, 12 | regex(settings) { 13 | return new RegExp(`^${settings.prefix}(?:setuser|su) ([^\s]*) (.*)`, `gi`); 14 | }, 15 | expectsUserInRegexSlot: 1, 16 | async action({ msg, match, settings, typedUser }) { 17 | console.log(`${msg.guild 18 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 19 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Admin set user ${typedUser ? getLabelFromUser(typedUser) : match[1]} > ${match[2]} (${msg.author.username})`); 20 | if (!match[1] || !match[2]) { 21 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}setuser <@user> to set that user's timezone.`, false, settings); 22 | } 23 | if (!typedUser) { 24 | return (0, replyInChannel_1.send)(msg, `I couldn't find a user by the name ${match[1]}.`, false, settings); 25 | } 26 | const foundTimezone = await getTimezoneFromLocation(match[2]); 27 | if (!foundTimezone) 28 | return (0, replyInChannel_1.send)(msg, `Sorry, I couldn't find a timezone for ${match[2]}.`, false, settings); 29 | await firestore_1.default.updateUserInGuild({ 30 | guildId: msg.guild.id, 31 | guildName: msg.guild.name, 32 | userId: typedUser.id || typedUser.user.id, 33 | updatedInfo: foundTimezone, 34 | }); 35 | (0, replyInChannel_1.send)(msg, `Timezone for ${getLabelFromUser(typedUser)} set to ${foundTimezone.timezoneName} by admin. (${getLightEmoji(foundTimezone.location)}${currentTimeAt(foundTimezone.location, false, settings.format24)})`, false, settings); 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /src/commands/setuser.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | import { 5 | getLabelFromUser, 6 | getLightEmoji, 7 | currentTimeAt, 8 | } from '../scripts/commonFunctions' 9 | import getTimezoneFromLocation from '../actions/getTimezoneFromLocation' 10 | 11 | export default { 12 | admin: true, 13 | regex(settings: Settings) { 14 | return new RegExp( 15 | `^${settings.prefix}(?:setuser|su) ([^\s]*) (.*)`, // eslint-disable-line 16 | `gi`, 17 | ) 18 | }, 19 | expectsUserInRegexSlot: 1, 20 | async action({ 21 | msg, 22 | match, 23 | settings, 24 | typedUser, 25 | }: ActionProps) { 26 | console.log( 27 | `${ 28 | msg.guild?.name 29 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 30 | : `Private Message` 31 | }${ 32 | msg.guild ? ` (${msg.guild.id})` : `` 33 | } - Admin set user ${ 34 | typedUser ? getLabelFromUser(typedUser) : match[1] 35 | } > ${match[2]} (${msg.author.username})`, 36 | ) 37 | if (!match[1] || !match[2]) { 38 | return send( 39 | msg, 40 | `Use this command in the format ${settings.prefix}setuser <@user> to set that user's timezone.`, 41 | false, 42 | settings, 43 | ) 44 | } 45 | if (!typedUser) { 46 | return send( 47 | msg, 48 | `I couldn't find a user by the name ${match[1]}.`, 49 | false, 50 | settings, 51 | ) 52 | } 53 | 54 | const foundTimezone = await getTimezoneFromLocation( 55 | match[2], 56 | ) 57 | if (!foundTimezone) 58 | return send( 59 | msg, 60 | `Sorry, I couldn't find a timezone for ${match[2]}.`, 61 | false, 62 | settings, 63 | ) 64 | 65 | await db.updateUserInGuild({ 66 | guildId: msg.guild?.id, 67 | guildName: msg.guild?.name, 68 | userId: typedUser.id || typedUser.user.id, 69 | updatedInfo: foundTimezone, 70 | }) 71 | 72 | send( 73 | msg, 74 | `Timezone for ${getLabelFromUser(typedUser)} set to ${ 75 | foundTimezone.timezoneName 76 | } by admin. (${getLightEmoji( 77 | foundTimezone.location, 78 | )}${currentTimeAt( 79 | foundTimezone.location, 80 | false, 81 | Boolean(settings.format24), 82 | )})`, 83 | false, 84 | settings, 85 | ) 86 | }, 87 | } 88 | -------------------------------------------------------------------------------- /dist/commands/setuser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const commonFunctions_1 = require("../scripts/commonFunctions"); 9 | const getTimezoneFromLocation_1 = __importDefault(require("../actions/getTimezoneFromLocation")); 10 | exports.default = { 11 | admin: true, 12 | regex(settings) { 13 | return new RegExp(`^${settings.prefix}(?:setuser|su) ([^\s]*) (.*)`, // eslint-disable-line 14 | `gi`); 15 | }, 16 | expectsUserInRegexSlot: 1, 17 | async action({ msg, match, settings, typedUser, }) { 18 | var _a, _b, _c; 19 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 20 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 21 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Admin set user ${typedUser ? (0, commonFunctions_1.getLabelFromUser)(typedUser) : match[1]} > ${match[2]} (${msg.author.username})`); 22 | if (!match[1] || !match[2]) { 23 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}setuser <@user> to set that user's timezone.`, false, settings); 24 | } 25 | if (!typedUser) { 26 | return (0, replyInChannel_1.send)(msg, `I couldn't find a user by the name ${match[1]}.`, false, settings); 27 | } 28 | const foundTimezone = await (0, getTimezoneFromLocation_1.default)(match[2]); 29 | if (!foundTimezone) 30 | return (0, replyInChannel_1.send)(msg, `Sorry, I couldn't find a timezone for ${match[2]}.`, false, settings); 31 | await firestore_1.default.updateUserInGuild({ 32 | guildId: (_b = msg.guild) === null || _b === void 0 ? void 0 : _b.id, 33 | guildName: (_c = msg.guild) === null || _c === void 0 ? void 0 : _c.name, 34 | userId: typedUser.id || typedUser.user.id, 35 | updatedInfo: foundTimezone, 36 | }); 37 | (0, replyInChannel_1.send)(msg, `Timezone for ${(0, commonFunctions_1.getLabelFromUser)(typedUser)} set to ${foundTimezone.timezoneName} by admin. (${(0, commonFunctions_1.getLightEmoji)(foundTimezone.location)}${(0, commonFunctions_1.currentTimeAt)(foundTimezone.location, false, Boolean(settings.format24))})`, false, settings); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/actions/getTimezoneFromLocation.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import db from '../db/firestore' 3 | import { standardizeTimezoneName } from '../scripts/commonFunctions' 4 | import timezoneCodeToLocation from '../scripts/timezoneCodeToLocationData' 5 | 6 | const geocodeUrlBase = `https://maps.googleapis.com/maps/api/geocode/json?key=${process.env.GOOGLE_API_KEY}` 7 | const timezoneUrlBase = `https://maps.googleapis.com/maps/api/timezone/json?key=${process.env.GOOGLE_API_KEY}` 8 | 9 | export default function ( 10 | location: string, 11 | ): Promise { 12 | return new Promise(async (resolve, reject) => { 13 | if (!location) { 14 | resolve(null) 15 | return 16 | } 17 | 18 | location = location 19 | .replace(/[<>\[\]()]/gi, ``) //eslint-disable-line 20 | .replace(/[_ ]+/gi, ` `) //eslint-disable-line 21 | .replace(/[@!?\d]*/gi, ``) 22 | 23 | const timezoneCodeLocationData = 24 | timezoneCodeToLocation(location) 25 | if (timezoneCodeLocationData) { 26 | resolve(timezoneCodeLocationData) 27 | return 28 | } 29 | 30 | const savedData = await db.getLocation(location) 31 | if (savedData) { 32 | resolve(savedData) 33 | return 34 | } 35 | try { 36 | console.log(`Making new API request for ${location}`) 37 | const foundLatLon = await axios 38 | .get(`${geocodeUrlBase}&address=${location}`) 39 | .then((res) => 40 | res.data.results 41 | ? res.data.results[0].geometry.location 42 | : null, 43 | ) 44 | .catch((e) => console.log) 45 | if (!foundLatLon) { 46 | resolve(null) 47 | return 48 | } 49 | const foundTimezone = await axios 50 | .get( 51 | `${timezoneUrlBase}&location=${foundLatLon.lat},${ 52 | foundLatLon.lng 53 | }×tamp=${Date.now() / 1000}`, 54 | ) 55 | .then((res) => res.data) 56 | .catch((e) => console.log) 57 | if (foundTimezone.status === `OK`) { 58 | const result = { 59 | timezoneName: standardizeTimezoneName( 60 | foundTimezone.timeZoneName, 61 | ), 62 | location: foundTimezone.timeZoneId, 63 | } 64 | db.setLocation({ 65 | locationName: location, 66 | locationSettings: result, 67 | }) 68 | resolve(result) 69 | return 70 | } 71 | resolve(null) 72 | } catch (e) { 73 | resolve(null) 74 | console.log(`Google API get error:`, e) 75 | } 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /src/commands/setprefix.ts: -------------------------------------------------------------------------------- 1 | import db from '../db/firestore' 2 | import { send } from '../actions/replyInChannel' 3 | import type { ActionProps } from '../../@types/command' 4 | 5 | export default { 6 | admin: true, 7 | regex(settings: Settings) { 8 | return new RegExp( 9 | `^(?:${settings.prefix}|t!)(?:prefix|setprefix|p)( ?)(.*)`, 10 | `gi`, 11 | ) 12 | }, 13 | async action({ msg, settings, match }: ActionProps) { 14 | console.log( 15 | `${ 16 | msg.guild?.name 17 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 18 | : `Private Message` 19 | }${ 20 | msg.guild ? ` (${msg.guild.id})` : `` 21 | } - Prefix > ${match[2]} (${msg.author.username})`, 22 | ) 23 | const previousPrefix = settings.prefix 24 | let newPrefix = match[2] 25 | if (!newPrefix || !match[1]) 26 | return send( 27 | msg, 28 | `The current prefix is: \`${settings.prefix}\` 29 | Type \`${settings.prefix}prefix \` to change the command prefix for this bot.`, 30 | `none`, 31 | settings, 32 | ) 33 | 34 | const illegalCharacters = [ 35 | `?`, 36 | `\\`, 37 | `^`, 38 | `$`, 39 | `@`, 40 | `#`, 41 | `{`, 42 | `}`, 43 | `[`, 44 | `]`, 45 | `(`, 46 | `)`, 47 | `<`, 48 | `>`, 49 | `:`, 50 | `*`, 51 | `|`, 52 | `+`, 53 | `.`, 54 | `\``, 55 | ] 56 | let foundIllegalCharacter: boolean | string = false 57 | for (let char of illegalCharacters) 58 | if (newPrefix.indexOf(char) > -1) 59 | foundIllegalCharacter = char 60 | if (foundIllegalCharacter === `\``) 61 | return send( 62 | msg, 63 | `The backtick character is not allowed in prefixes. Please try a different prefix. 64 | (Disallowed characters are \`${illegalCharacters.join( 65 | ``, 66 | )} and the backtick character. Your prefix has not been changed.)`, 67 | `none`, 68 | settings, 69 | ) 70 | if (foundIllegalCharacter) 71 | return send( 72 | msg, 73 | `The character \`${foundIllegalCharacter}\` is not allowed in prefixes. Please try a different prefix. 74 | (Disallowed characters are \`${illegalCharacters.join( 75 | ``, 76 | )} and the backtick character. Your prefix has not been changed.)`, 77 | `none`, 78 | settings, 79 | ) 80 | 81 | newPrefix = newPrefix.substring(0, 12) 82 | await db.setGuildSettings( 83 | { 84 | prefix: newPrefix, 85 | }, 86 | msg.guild?.id, 87 | ) 88 | 89 | send( 90 | msg, 91 | `The timezone command prefix been changed from \`${previousPrefix}\` to \`${newPrefix}\``, 92 | `none`, 93 | settings, 94 | ) 95 | }, 96 | } 97 | -------------------------------------------------------------------------------- /dist/src/commands/setprefix.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | module.exports = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^(?:${settings.prefix}|t!)(?:prefix|setprefix|p)( ?)(.*)`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | console.log(`${msg.guild 15 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 16 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Prefix > ${match[2]} (${msg.author.username})`); 17 | const previousPrefix = settings.prefix; 18 | let newPrefix = match[2]; 19 | if (!newPrefix || !match[1]) 20 | return (0, replyInChannel_1.send)(msg, `The current prefix is: \`${settings.prefix}\` 21 | Type \`${settings.prefix}prefix \` to change the command prefix for this bot.`, `none`, settings); 22 | const illegalCharacters = [ 23 | `?`, 24 | `\\`, 25 | `^`, 26 | `$`, 27 | `@`, 28 | `#`, 29 | `{`, 30 | `}`, 31 | `[`, 32 | `]`, 33 | `(`, 34 | `)`, 35 | `<`, 36 | `>`, 37 | `:`, 38 | `*`, 39 | `|`, 40 | `+`, 41 | `.`, 42 | `\``, 43 | ]; 44 | let foundIllegalCharacter = false; 45 | for (let char of illegalCharacters) 46 | if (newPrefix.indexOf(char) > -1) 47 | foundIllegalCharacter = char; 48 | if (foundIllegalCharacter === `\``) 49 | return (0, replyInChannel_1.send)(msg, `The backtick character is not allowed in prefixes. Please try a different prefix. 50 | (Disallowed characters are \`${illegalCharacters.join(``)} and the backtick character. Your prefix has not been changed.)`, `none`, settings); 51 | if (foundIllegalCharacter) 52 | return (0, replyInChannel_1.send)(msg, `The character \`${foundIllegalCharacter}\` is not allowed in prefixes. Please try a different prefix. 53 | (Disallowed characters are \`${illegalCharacters.join(``)} and the backtick character. Your prefix has not been changed.)`, `none`, settings); 54 | newPrefix = newPrefix.substring(0, 12); 55 | await firestore_1.default.setGuildSettings({ 56 | guildId: msg.guild.id, 57 | prefix: newPrefix, 58 | }); 59 | (0, replyInChannel_1.send)(msg, `The timezone command prefix been changed from \`${previousPrefix}\` to \`${newPrefix}\``, `none`, settings); 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /dist/src/actions/getTimezoneFromLocation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const axios_1 = __importDefault(require("axios")); 7 | const firestore_1 = __importDefault(require("../db/firestore")); 8 | const commonFunctions_1 = require("../scripts/commonFunctions"); 9 | const timezoneCodeToLocationData_1 = __importDefault(require("../scripts/timezoneCodeToLocationData")); 10 | const geocodeUrlBase = `https://maps.googleapis.com/maps/api/geocode/json?key=${process.env.GOOGLE_API_KEY}`; 11 | const timezoneUrlBase = `https://maps.googleapis.com/maps/api/timezone/json?key=${process.env.GOOGLE_API_KEY}`; 12 | function default_1(location) { 13 | if (!location) 14 | return; 15 | location = location 16 | .replace(/[<>\[\]()]/gi, ``) //eslint-disable-line 17 | .replace(/[_ ]/gi, ` `) //eslint-disable-line 18 | .replace(/@!?\d* */gi, ``); 19 | const timezoneCodeLocationData = (0, timezoneCodeToLocationData_1.default)(location); 20 | if (timezoneCodeLocationData) 21 | return timezoneCodeLocationData; 22 | return new Promise(async (resolve, reject) => { 23 | const savedData = await firestore_1.default.getLocation(location); 24 | if (savedData) { 25 | resolve(savedData); 26 | return; 27 | } 28 | try { 29 | console.log(`Making new API request for ${location}`); 30 | const foundLatLon = await axios_1.default 31 | .get(`${geocodeUrlBase}&address=${location}`) 32 | .then((res) => res.data.results 33 | ? res.data.results[0].geometry.location 34 | : null) 35 | .catch((e) => console.log); 36 | if (!foundLatLon) 37 | resolve(null); 38 | const foundTimezone = await axios_1.default 39 | .get(`${timezoneUrlBase}&location=${foundLatLon.lat},${foundLatLon.lng}×tamp=${Date.now() / 1000}`) 40 | .then((res) => res.data) 41 | .catch((e) => console.log); 42 | if (foundTimezone.status === `OK`) { 43 | const result = { 44 | timezoneName: (0, commonFunctions_1.standardizeTimezoneName)(foundTimezone.timeZoneName), 45 | location: foundTimezone.timeZoneId, 46 | }; 47 | firestore_1.default.setLocation({ 48 | locationName: location, 49 | locationSettings: result, 50 | }); 51 | resolve(result); 52 | return; 53 | } 54 | resolve(null); 55 | } 56 | catch (e) { 57 | resolve(null); 58 | console.log(`Google API get error:`, e); 59 | } 60 | }); 61 | } 62 | exports.default = default_1; 63 | -------------------------------------------------------------------------------- /dist/commands/setprefix.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | exports.default = { 9 | admin: true, 10 | regex(settings) { 11 | return new RegExp(`^(?:${settings.prefix}|t!)(?:prefix|setprefix|p)( ?)(.*)`, `gi`); 12 | }, 13 | async action({ msg, settings, match }) { 14 | var _a, _b; 15 | console.log(`${((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.name) 16 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 17 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - Prefix > ${match[2]} (${msg.author.username})`); 18 | const previousPrefix = settings.prefix; 19 | let newPrefix = match[2]; 20 | if (!newPrefix || !match[1]) 21 | return (0, replyInChannel_1.send)(msg, `The current prefix is: \`${settings.prefix}\` 22 | Type \`${settings.prefix}prefix \` to change the command prefix for this bot.`, `none`, settings); 23 | const illegalCharacters = [ 24 | `?`, 25 | `\\`, 26 | `^`, 27 | `$`, 28 | `@`, 29 | `#`, 30 | `{`, 31 | `}`, 32 | `[`, 33 | `]`, 34 | `(`, 35 | `)`, 36 | `<`, 37 | `>`, 38 | `:`, 39 | `*`, 40 | `|`, 41 | `+`, 42 | `.`, 43 | `\``, 44 | ]; 45 | let foundIllegalCharacter = false; 46 | for (let char of illegalCharacters) 47 | if (newPrefix.indexOf(char) > -1) 48 | foundIllegalCharacter = char; 49 | if (foundIllegalCharacter === `\``) 50 | return (0, replyInChannel_1.send)(msg, `The backtick character is not allowed in prefixes. Please try a different prefix. 51 | (Disallowed characters are \`${illegalCharacters.join(``)} and the backtick character. Your prefix has not been changed.)`, `none`, settings); 52 | if (foundIllegalCharacter) 53 | return (0, replyInChannel_1.send)(msg, `The character \`${foundIllegalCharacter}\` is not allowed in prefixes. Please try a different prefix. 54 | (Disallowed characters are \`${illegalCharacters.join(``)} and the backtick character. Your prefix has not been changed.)`, `none`, settings); 55 | newPrefix = newPrefix.substring(0, 12); 56 | await firestore_1.default.setGuildSettings({ 57 | prefix: newPrefix, 58 | }, (_b = msg.guild) === null || _b === void 0 ? void 0 : _b.id); 59 | (0, replyInChannel_1.send)(msg, `The timezone command prefix been changed from \`${previousPrefix}\` to \`${newPrefix}\``, `none`, settings); 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /dist/actions/getTimezoneFromLocation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const axios_1 = __importDefault(require("axios")); 7 | const firestore_1 = __importDefault(require("../db/firestore")); 8 | const commonFunctions_1 = require("../scripts/commonFunctions"); 9 | const timezoneCodeToLocationData_1 = __importDefault(require("../scripts/timezoneCodeToLocationData")); 10 | const geocodeUrlBase = `https://maps.googleapis.com/maps/api/geocode/json?key=${process.env.GOOGLE_API_KEY}`; 11 | const timezoneUrlBase = `https://maps.googleapis.com/maps/api/timezone/json?key=${process.env.GOOGLE_API_KEY}`; 12 | function default_1(location) { 13 | return new Promise(async (resolve, reject) => { 14 | if (!location) { 15 | resolve(null); 16 | return; 17 | } 18 | location = location 19 | .replace(/[<>\[\]()]/gi, ``) //eslint-disable-line 20 | .replace(/[_ ]+/gi, ` `) //eslint-disable-line 21 | .replace(/[@!?\d]*/gi, ``); 22 | const timezoneCodeLocationData = (0, timezoneCodeToLocationData_1.default)(location); 23 | if (timezoneCodeLocationData) { 24 | resolve(timezoneCodeLocationData); 25 | return; 26 | } 27 | const savedData = await firestore_1.default.getLocation(location); 28 | if (savedData) { 29 | resolve(savedData); 30 | return; 31 | } 32 | try { 33 | console.log(`Making new API request for ${location}`); 34 | const foundLatLon = await axios_1.default 35 | .get(`${geocodeUrlBase}&address=${location}`) 36 | .then((res) => res.data.results 37 | ? res.data.results[0].geometry.location 38 | : null) 39 | .catch((e) => console.log); 40 | if (!foundLatLon) { 41 | resolve(null); 42 | return; 43 | } 44 | const foundTimezone = await axios_1.default 45 | .get(`${timezoneUrlBase}&location=${foundLatLon.lat},${foundLatLon.lng}×tamp=${Date.now() / 1000}`) 46 | .then((res) => res.data) 47 | .catch((e) => console.log); 48 | if (foundTimezone.status === `OK`) { 49 | const result = { 50 | timezoneName: (0, commonFunctions_1.standardizeTimezoneName)(foundTimezone.timeZoneName), 51 | location: foundTimezone.timeZoneId, 52 | }; 53 | firestore_1.default.setLocation({ 54 | locationName: location, 55 | locationSettings: result, 56 | }); 57 | resolve(result); 58 | return; 59 | } 60 | resolve(null); 61 | } 62 | catch (e) { 63 | resolve(null); 64 | console.log(`Google API get error:`, e); 65 | } 66 | }); 67 | } 68 | exports.default = default_1; 69 | -------------------------------------------------------------------------------- /dist/src/commands/set.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const firestore_1 = __importDefault(require("../db/firestore")); 7 | const replyInChannel_1 = require("../actions/replyInChannel"); 8 | const getTimezoneFromLocation = require(`../actions/getTimezoneFromLocation`); 9 | const { getUserInGuildFromId, getLightEmoji, currentTimeAt, getLabelFromUser, standardizeTimezoneName, } = require(`../scripts/commonFunctions`); 10 | const allCommands = require(`./index`); 11 | module.exports = { 12 | regex(settings) { 13 | return new RegExp(`^${settings.prefix}(?:set|s)(?!user) (.*)$`, `gi`); 14 | }, 15 | async action({ msg, settings, match, client }) { 16 | if (!match[1]) 17 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}set to set your timezone.`, false, settings); 18 | // admin accidentally used this command to try to set someone 19 | const hasAt = match[1].indexOf(`<@`) >= 0; 20 | const hasSpaceAfterAt = match[1].lastIndexOf(` `) > hasAt; 21 | if (hasAt && hasSpaceAfterAt) { 22 | const commandRegex = new RegExp(`${settings.prefix}[^ ]* `, `gi`); 23 | msg.content = msg.content.replace(commandRegex, `${settings.prefix}setuser `); 24 | return allCommands(msg, settings, client); 25 | } 26 | else if (hasAt) 27 | return (0, replyInChannel_1.send)(msg, `Use this command in the format ${settings.prefix}set to set your timezone.`, false, settings); 28 | console.log(`${msg.guild 29 | ? msg.guild.name.substring(0, 25).padEnd(25, ` `) 30 | : `Private Message`}${msg.guild ? ` (${msg.guild.id})` : ``} - ${msg.author.username} > set to ${match[1]}`); 31 | const foundTimezone = await getTimezoneFromLocation(match[1]); 32 | if (!foundTimezone) 33 | return (0, replyInChannel_1.send)(msg, `Sorry, I couldn't find a timezone for ${match[1]}.`, false, settings); 34 | await firestore_1.default.updateUserInGuild({ 35 | guildId: msg.guild.id, 36 | guildName: msg.guild.name, 37 | userId: msg.author.id, 38 | updatedInfo: foundTimezone, 39 | }); 40 | const authorInGuild = await getUserInGuildFromId(msg.guild, msg.author.id); 41 | (0, replyInChannel_1.send)(msg, `Timezone for ${getLabelFromUser(authorInGuild)} set to ${standardizeTimezoneName(foundTimezone.timezoneName)}. (${getLightEmoji(foundTimezone.location)}${currentTimeAt(foundTimezone.location, false, settings.format24)})` + 42 | (match[1].length <= 4 || 43 | (match[1].length <= 7 && 44 | match[1].indexOf(`+`) > -1) || 45 | (match[1].length <= 7 && 46 | match[1].indexOf(`-`) > -1) || 47 | match[1].toLowerCase().indexOf(` time`) > -1 48 | ? `\nBy the way, location names always work better than timezone codes/names!` 49 | : ``), false, settings); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # timezone-bot 2 | 3 | A simple timezone bot for Discord servers. 4 | 5 | ### Update Jan 2022: 6 | > tl;dr the public TimezoneBot is shutting down because Discord neutered it. 7 | 8 | > Discord changed their bot rules such that seeing message contents requires special permission. I applied for this permission, and was rejected with illogical reasoning. Since one of the primary purposes of this bot — to auto-reply to @messages — will become impossible, I've decided to shut down the publically hosted bot rather than continue to spend money on hosting a husk of the bot I've envisioned. 9 | 10 | > But! The good news is, you're welcome to run your own copy of the bot, which will work fully as envisioned as long as you don't pass 100 servers! The guide for self-hosting is available here: https://github.com/midblue/timezone-bot/blob/master/instructions%20selfhosting.md 11 | 12 | > Please be aware that I will not be actively developing the bot going forward. Feel free to fork your own versions for new features. 13 | 14 | > I'd like to give a special shout out to all the fantastic people who have supported the bot's development, and all of the great folks on the support server and elsewhere who have given advice, input, and support along the way. 15 | Thanks for understanding, and have a great day, wherever in the world you may be! 16 | 17 | --- 18 | 19 | ![](https://www.jasperstephenson.com/posts/timezonebot/full/1.png) 20 | Allows users to set their timezone, then passively notes timezones when appropriate. 21 | 22 | ## Commands: 23 | 24 | - `t!time ` to see the current time for a specific user or in a specific place. 25 | - `t!timein ` to see the current time in a specific place. 26 | - `t!set ` to set your own timezone. (UTC codes also work, e.g. 'UTC+3', 'UTC-8') 27 | - `t!users` or `t!all` to see all users' set timezones. (use `t!here` to restrict to the current channel) 28 | - `t!count` to see timezone counts. (`t!count here` works) 29 | - `t!role <@role or role name>`to see the timezones for all users in a role. 30 | - `t!at