Skip to content

Commit

Permalink
/timezone command, allow to set your TZ & compare with others (#68)
Browse files Browse the repository at this point in the history
* feat: adds timezone command, allows to set your TZ & compare with another user
  • Loading branch information
en3sis authored Aug 14, 2023
1 parent 4ff9f53 commit bbd830f
Show file tree
Hide file tree
Showing 16 changed files with 1,162 additions and 434 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"no-trailing-spaces": 1,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-extra-semi": 0,
"@typescript-eslint/no-explicit-any": 1,
"@typescript-eslint/no-unused-vars": 1,
"@typescript-eslint/no-non-null-assertion": 0
// "eslint@typescript-eslint/no-explicit-any": 0
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
/node_modules/
/src/config.ts
build/
package-lock.json
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ISDEV",
"kubectl",
"Kubernetes",
"luxon",
"MYAPP",
"openai",
"perma",
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Before running any command, run `npm install` & `cp .env.template .env`, fill al
There's a `supabase/schema.sql` file with the necessary tables and columns needed for the bot to work. You can use it to create the tables in your Supabase instance.

More information related to working with Supabase local development can be found [📹 here 🔗](https://www.youtube.com/watch?v=N0Wb85m3YMI)

## 👩🏼‍💻 Development

### `npm run dev`
Expand Down
15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"start": "node build/utils/deploy-commands.js && node build/index.js",
"dev": "ISDEV=true nodemon hans",
"build": "tsc --project ./",
"types:local": "supabase gen types typescript --local --schema public > src/types/database.types.ts",
"build_dev": "rm -rf ./build && tsc --project ./",
"lint": "eslint . --fix",
"format": "npx prettier --write '**/*.ts'",
Expand All @@ -23,7 +24,7 @@
},
"license": "MIT",
"devDependencies": {
"@discordjs/rest": "^1.7.1",
"@discordjs/rest": "^2.0.0",
"@types/chai": "^4.3.5",
"@types/expect": "^24.3.0",
"@types/lodash": "^4.14.184",
Expand All @@ -32,12 +33,12 @@
"@types/node-cron": "^3.0.4",
"@types/sentiment": "^5.0.1",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-prettier": "^9.0.0",
"husky": "^8.0.1",
"nodemon": "^2.0.19",
"nodemon": "^3.0.1",
"prettier": "^2.7.1",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
Expand All @@ -50,6 +51,7 @@
"axios": "^1.4.0",
"chai": "^4.3.6",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
"discord.js": "^14.11.0",
"dotenv": "^16.0.2",
"lodash": "^4.17.21",
Expand All @@ -58,6 +60,7 @@
"node-cron": "^3.0.2",
"openai": "^3.2.1",
"sentiment": "^5.0.2",
"socket.io-client": "^4.5.1"
"socket.io-client": "^4.5.1",
"supabase": "^1.83.7"
}
}
9 changes: 2 additions & 7 deletions src/commands/about.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SlashCommandBuilder } from '@discordjs/builders'
import { CommandInteraction, GuildMember } from 'discord.js'
import { getUserInformation } from '../controllers/engagement/user-info.controller'
import { logger } from '../utils/debugging'
import { extractUser } from '../utils/users'

// https://discord.js.org/#/docs/main/stable/class/CommandInteraction?scrollTo=replied
module.exports = {
Expand All @@ -21,13 +22,7 @@ module.exports = {
content: 'Please provide a user',
})

let _member: string

if (/<@!?\d+>/g.test(user)) {
_member = user.split(`<@!`)[1].replace('>', '')
} else {
_member = user
}
const _member = extractUser(user)

const member: GuildMember = interaction.guild.members.cache.get(_member)

Expand Down
51 changes: 51 additions & 0 deletions src/commands/timezones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { SlashCommandBuilder } from '@discordjs/builders'
import { CommandInteraction } from 'discord.js'
import { timezonesController } from '../controllers/plugins/timezones.controller'
import { logger } from '../utils/debugging'

module.exports = {
ephemeral: true,
data: new SlashCommandBuilder()
.setName('timezone')
.setDescription('Set your timezone & get the time of another user')
.setDefaultMemberPermissions('0')
.addSubcommand((command) =>
command
.setName('set')
.setDescription('Set your timezone')
.addStringOption((option) =>
option.setName('zone').setDescription('Your timezone(TZ Identifier)').setRequired(true),
),
)
.addSubcommand((command) =>
command
.setName('difference')
.setDescription('Shows the differences between your timezone and another user')
.addUserOption((option) =>
option.setName('user').setDescription('@username').setRequired(true),
),
)
.addSubcommand((command) =>
command.setName('list').setDescription('Lists all the available timezones'),
),
async execute(interaction: CommandInteraction) {
try {
if (!interaction.isChatInputCommand()) return

if (interaction.options.getSubcommand() === 'list') {
return interaction.editReply({
embeds: [
{
title: 'Timezones list',
description: `Find your timezone 🔗[here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) and copy the data on the **TZ Identifier**`,
},
],
})
} else {
await timezonesController(interaction)
}
} catch (error) {
logger('❌ Command: timezone: ', error)
}
},
}
3 changes: 1 addition & 2 deletions src/controllers/bot/plugins.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export type GuildPluginData = {
}

/**
*
* @param guild_id
* @param pluginName
* @returns {enabled: false, metadata: {}, data: {}
Expand All @@ -86,7 +85,7 @@ export const resolveGuildPlugins = async (
(ele: GuildPlugin) => ele.name === pluginName,
)

if (matchingPlugin && matchingPlugin.plugins.enabled && matchingPlugin.enabled) {
if (matchingPlugin && matchingPlugin.plugins[0].enabled && matchingPlugin.enabled) {
return {
enabled: matchingPlugin.enabled || false,
metadata: JSON.parse(JSON.stringify(matchingPlugin.metadata)),
Expand Down
132 changes: 132 additions & 0 deletions src/controllers/plugins/timezones.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import formatDistance from 'date-fns/formatDistance'
import { ChatInputCommandInteraction, Colors } from 'discord.js'
import { TIMEZONES_LIST } from '../../data/timezones'
import supabase from '../../libs/supabase'
import { extractHours, getTimeZonesTime } from '../../utils/dates'
import { TIME_ZONES_REGEX } from '../../utils/regex'

export const timezonesController = async (interaction: ChatInputCommandInteraction) => {
try {
const command = interaction.options.getSubcommand()
if (command === 'set') {
const timezone = interaction.options.getString('zone', true).trim()

if (!TIME_ZONES_REGEX.test(timezone) || !TIMEZONES_LIST.includes(timezone)) {
return interaction.editReply({
embeds: [
{
title: '❗️ Invalid timezone',
description: `The timezone **${timezone}** you provided is invalid. Please use the following format: **America/New_York**. You can find a list of valid timezones [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)`,
color: Colors.Red,
},
],
})
}

// Check if user already has a timezone set
const { data } = await supabase
.from('users_settings')
.select('*')
.eq('user_id', interaction.user.id)
.eq('type', 'timezone')
.single()

// If not, insert it, otherwise update it
if (!data) {
await supabase
.from('users_settings')
.insert({
user_id: interaction.user.id,
type: 'timezone',
metadata: { timezone },
})
.select()
} else {
await supabase
.from('users_settings')
.update({
metadata: { timezone },
})
.eq('user_id', interaction.user.id)
.eq('type', 'timezone')
}

interaction.editReply({
embeds: [
{
title: 'Timezone set',
description: `Your timezone has been set to **${timezone}**`,
},
],
})
} else if (command === 'compare') {
// Get the target & author user
const targetUser = interaction.options.getUser('user', true)
const authorUser = interaction.user

// Check if both users have a timezone set
const { data } = await supabase
.from('users_settings')
.select('*')
.in('user_id', [targetUser.id, authorUser.id])

const targetUserData = data.filter((d) => d.user_id === targetUser.id)[0]
const authorUserData = data.filter((d) => d.user_id === authorUser.id)[0]

// If both users have a timezone set, compare them
if (data[0] && data[1]) {
const targetUserTimezone = (targetUserData.metadata as { timezone?: string })?.timezone
const authorTimezone = (authorUserData.metadata as { timezone?: string })?.timezone

const getTimeZones = getTimeZonesTime(targetUserTimezone, authorTimezone)

const timeDifference = formatDistance(
new Date(getTimeZones.authorLocalTime),
new Date(getTimeZones.targetLocalTime),
)

const targetTimezoneIsInFuture =
new Date(getTimeZones.targetLocalTime) > new Date(getTimeZones.authorLocalTime)

return interaction.editReply({
embeds: [
{
title: '🕑 Timezone difference',
description: `**${targetUser.username}** is currently **${timeDifference} ${
targetTimezoneIsInFuture ? 'ahead' : 'behind'
}** with his local time being **${extractHours(
getTimeZones.targetLocalTime,
)}** and yours being **${extractHours(getTimeZones.authorLocalTime)}**`,
fields: [
{
name: 'Your timezone',
value: `${authorTimezone} (${extractHours(getTimeZones.authorLocalTime)})`,
inline: true,
},
{
name: `${targetUser.username}'s timezone`,
value: `${targetUserTimezone} (${extractHours(getTimeZones.targetLocalTime)})`,
inline: true,
},
],
color: Colors.Green,
},
],
})
} else {
return interaction.editReply({
embeds: [
{
title: '❗️ Timezone not set',
description: `${targetUser.username} has not set their timezone yet.`,
color: Colors.Red,
},
],
})
}
}
} catch (error) {
console.log('💢 ERROR: timezonesController(): ', error)
throw Error(error.message)
}
}
Loading

0 comments on commit bbd830f

Please sign in to comment.