Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin & Command: /summarize #55

Merged
merged 4 commits into from
Jun 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
ISDEV=true

#=========== 🔅 REQUIRED ===========#
DISCORD_TOKEN=
DISCORD_CLIENT_ID=
Expand All @@ -11,8 +9,11 @@ SUPABASE_SERVICE_ROL=
BOT_GUILD_ID= # Your Discord Guild ID, the home of your Bot.


#=========== 💠 PLUGINS ===========#
# SERVICES, not required but some functionality will be limited
ISDEV=
GITHUB_API_TOKEN=
WEATHER_API= # Grab one at https://openweathermap.org/api
OPENAI_API_KEY=
OPENAI_ORGANIZATION_ID=
HUGGINGFACE_API_KEY=
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"CHATGPT",
"Cooldown",
"davinci",
"huggingface",
"ISDEV",
"kubectl",
"Kubernetes",
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
"@discordjs/rest": "^1.7.1",
"@types/chai": "^4.3.5",
"@types/expect": "^24.3.0",
"@types/lodash": "^4.14.184",
"@types/mocha": "^10.0.1",
"@types/node": "^20.1.0",
"@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",
"@types/lodash": "^4.14.184",
"@types/node-cron": "^3.0.4",
"eslint": "^8.40.0",
"eslint-config-prettier": "^8.5.0",
"husky": "^8.0.1",
Expand All @@ -45,6 +45,7 @@
"typescript": "^5.0.4"
},
"dependencies": {
"@huggingface/inference": "^2.5.0",
"@supabase/supabase-js": "^2.21.0",
"axios": "^1.4.0",
"chai": "^4.3.6",
Expand Down
23 changes: 23 additions & 0 deletions src/commands/summarize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SlashCommandBuilder } from '@discordjs/builders'
import { CommandInteraction } from 'discord.js'
import { summarizeController } from '../controllers/plugins/summarize.controller'

// https://discord.js.org/#/docs/main/stable/class/CommandInteraction?scrollTo=replied
module.exports = {
data: new SlashCommandBuilder()
.setName('summarize')
.setDescription('Uses Hans AI to summarize a text or message')
.addStringOption((string) =>
string.setName('prompt').setDescription('Text or message id to summarize').setRequired(true),
),
async execute(interaction: CommandInteraction) {
try {
await interaction.deferReply()

await summarizeController(interaction)
} catch (error) {
console.error('❌ tools.ts: ', error)
throw new Error(error)
}
},
}
6 changes: 6 additions & 0 deletions src/controllers/bot/plugins.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ export type GuildPluginData = {
data: GuildPlugin | any
}

/**
*
* @param guild_id
* @param pluginName
* @returns {enabled: false, metadata: {}, data: {}
*/
export const resolveGuildPlugins = async (
guild_id: string,
pluginName: string,
Expand Down
48 changes: 48 additions & 0 deletions src/controllers/plugins/summarize.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { CommandInteraction, TextChannel } from 'discord.js'
import { Hans } from '../..'
import { inference } from '../../libs/huggingface'
import { MESSAGE_ID_REGEX } from '../../utils/regex'
import { resolveGuildPlugins } from '../bot/plugins.controller'

export const summarizeController = async (interaction: CommandInteraction) => {
try {
if (!process.env.HUGGINGFACE_API_KEY) {
await interaction.editReply('Command could not be executed at this moment.')
throw new Error('💢 HUGGINGFACE_API_KEY is missing from the `.env` file.')
}

const guildPlugin = await resolveGuildPlugins(interaction.guildId!, 'summarize')

if (!guildPlugin.enabled)
await interaction.editReply('This feature is not enabled for this server.')

let text = interaction.options.get('prompt')!.value as string

if (text.match(MESSAGE_ID_REGEX)) {
const channel = Hans.channels.cache.get(interaction.channelId!) as TextChannel
text = channel.messages.cache.get(text)?.content as string

if (!text) {
await interaction.editReply(
`Message not found on this channel or ${Hans.user.username} has no permission to read it.`,
)
return
}
}

const { summary_text } = await inference.summarization({
model: 'facebook/bart-large-cnn',
parameters: {
max_length: 100,
},
inputs: text,
})

return await interaction.editReply({
content: summary_text,
})
} catch (error) {
console.error('❌ summarizeController(): ', error)
throw new Error(error)
}
}
3 changes: 3 additions & 0 deletions src/libs/huggingface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { HfInference } from '@huggingface/inference'

export const inference = new HfInference(process.env.HUGGINGFACE_API_KEY)
13 changes: 12 additions & 1 deletion src/models/plugins.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,18 @@ export const pluginsList: Record<string, GenericPluginParts> = {
premium: true,
category: 'productivity',
},

summarize: {
...genericStructure,
description: 'Summarizes a text or discord message using the facebook/bart-large-cnn model.',
premium: false,
category: 'productivity',
},
textClassification: {
...genericStructure,
description: 'Classifies a text, informs moderation if the sentiment is negative.',
premium: false,
enabled: false,
},
threads: {
...genericStructure,
description: 'Allows for the automatic creation of threads in a specific channel.',
Expand Down
2 changes: 1 addition & 1 deletion src/utils/regex.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const RedditTitleGroup = /\[(.*?)\]/g
export const ROLE_MENTION_REGEX = /@(\w+)/g
export const MESSAGE_ID_REGEX = /^(\d{19})$/
88 changes: 58 additions & 30 deletions supabase/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ create table "public"."configs" (
"monitoring_channel_id" text
);


alter table "public"."configs" enable row level security;
alter table
"public"."configs" enable row level security;

create table "public"."guilds" (
"id" integer generated by default as identity not null,
Expand All @@ -28,8 +28,8 @@ create table "public"."guilds" (
"created_at" text
);


alter table "public"."guilds" enable row level security;
alter table
"public"."guilds" enable row level security;

create table "public"."guilds_plugins" (
"id" integer generated by default as identity not null,
Expand All @@ -40,21 +40,21 @@ create table "public"."guilds_plugins" (
"created_at" text
);


alter table "public"."guilds_plugins" enable row level security;
alter table
"public"."guilds_plugins" enable row level security;

create table "public"."plugins" (
"id" integer generated by default as identity not null,
"name" text,
"description" text,
"enabled" boolean,
"premium" boolean,
"category" text default 'miscellaneous'::text,
"category" text default 'miscellaneous' :: text,
"created_at" text
);


alter table "public"."plugins" enable row level security;
alter table
"public"."plugins" enable row level security;

CREATE UNIQUE INDEX configs_pkey ON public.configs USING btree (id);

Expand All @@ -68,38 +68,66 @@ CREATE UNIQUE INDEX plugins_name_key ON public.plugins USING btree (name);

CREATE UNIQUE INDEX plugins_pkey ON public.plugins USING btree (id);

alter table "public"."configs" add constraint "configs_pkey" PRIMARY KEY using index "configs_pkey";
alter table
"public"."configs"
add
constraint "configs_pkey" PRIMARY KEY using index "configs_pkey";

alter table "public"."guilds" add constraint "guilds_pkey" PRIMARY KEY using index "guilds_pkey";
alter table
"public"."guilds"
add
constraint "guilds_pkey" PRIMARY KEY using index "guilds_pkey";

alter table "public"."guilds_plugins" add constraint "guilds_plugins_pkey" PRIMARY KEY using index "guilds_plugins_pkey";
alter table
"public"."guilds_plugins"
add
constraint "guilds_plugins_pkey" PRIMARY KEY using index "guilds_plugins_pkey";

alter table "public"."plugins" add constraint "plugins_pkey" PRIMARY KEY using index "plugins_pkey";
alter table
"public"."plugins"
add
constraint "plugins_pkey" PRIMARY KEY using index "plugins_pkey";

alter table "public"."guilds" add constraint "guilds_guild_id_key" UNIQUE using index "guilds_guild_id_key";
alter table
"public"."guilds"
add
constraint "guilds_guild_id_key" UNIQUE using index "guilds_guild_id_key";

alter table "public"."guilds_plugins" add constraint "guilds_plugins_name_fkey" FOREIGN KEY (name) REFERENCES plugins(name) not valid;
alter table
"public"."guilds_plugins"
add
constraint "guilds_plugins_name_fkey" FOREIGN KEY (name) REFERENCES plugins(name) not valid;

alter table "public"."guilds_plugins" validate constraint "guilds_plugins_name_fkey";
alter table
"public"."guilds_plugins" validate constraint "guilds_plugins_name_fkey";

alter table "public"."guilds_plugins" add constraint "guilds_plugins_owner_fkey" FOREIGN KEY (owner) REFERENCES guilds(guild_id) ON DELETE CASCADE not valid;
alter table
"public"."guilds_plugins"
add
constraint "guilds_plugins_owner_fkey" FOREIGN KEY (owner) REFERENCES guilds(guild_id) ON DELETE CASCADE not valid;

alter table "public"."guilds_plugins" validate constraint "guilds_plugins_owner_fkey";

alter table "public"."plugins" add constraint "plugins_name_key" UNIQUE using index "plugins_name_key";
alter table
"public"."guilds_plugins" validate constraint "guilds_plugins_owner_fkey";

alter table
"public"."plugins"
add
constraint "plugins_name_key" UNIQUE using index "plugins_name_key";

-- Functions
CREATE OR REPLACE FUNCTION reset_chat_gpt_plugin()
RETURNS void AS
$$
BEGIN
UPDATE guilds_plugins
SET metadata = jsonb_set(metadata, '{usage}', '100'::jsonb)
WHERE name = 'chatGtp';
CREATE
OR REPLACE FUNCTION reset_chat_gpt_plugin() RETURNS void AS $ $ BEGIN
UPDATE
guilds_plugins
SET
metadata = jsonb_set(metadata, '{usage}', '100' :: jsonb)
WHERE
name = 'chatGtp';

END;
$$
LANGUAGE plpgsql;

$ $ LANGUAGE plpgsql;

-- Crons
SELECT cron.schedule('0 0 * * *', 'SELECT reset_chat_gpt_plugin()');
SELECT
cron.schedule('0 0 * * *', 'SELECT reset_chat_gpt_plugin()');
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.40.0.tgz#3ba73359e11f5a7bd3e407f70b3528abfae69cec"
integrity sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==

"@huggingface/inference@^2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@huggingface/inference/-/inference-2.5.0.tgz#8e14ee6696e91aecb132c90d3b07be8373e70338"
integrity sha512-X3NSdrWAKNTLAsEKabH48Wc+Osys+S7ilRcH1bf9trSDmJlzPVXDseXMRBHCFPCYd5AAAIakhENO4zCqstVg8g==

"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz"
Expand Down