mirror of
https://github.com/xHyroM/bun-discord-bot.git
synced 2024-11-22 14:41:05 +01:00
feat: sqlite instead memory cache
This commit is contained in:
parent
73ad04a894
commit
6511816229
5 changed files with 113 additions and 75 deletions
BIN
files/database.sqlite
Normal file
BIN
files/database.sqlite
Normal file
Binary file not shown.
|
@ -23,8 +23,8 @@ new Command({
|
||||||
description: 'Issue numer/name, PR number/name or direct link to Github Issue or PR',
|
description: 'Issue numer/name, PR number/name or direct link to Github Issue or PR',
|
||||||
type: ApplicationCommandOptionType.String,
|
type: ApplicationCommandOptionType.String,
|
||||||
required: true,
|
required: true,
|
||||||
run: (ctx) => {
|
run: async(ctx) => {
|
||||||
return ctx.respond(search(ctx.value, ctx?.options?.[1]?.value as string || 'oven-sh/bun'));
|
return ctx.respond(await search(ctx.value, ctx?.options?.[1]?.value as string || 'oven-sh/bun'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ new Command({
|
||||||
const repositoryOwner = repositorySplit[0];
|
const repositoryOwner = repositorySplit[0];
|
||||||
const repositoryName = repositorySplit[1];
|
const repositoryName = repositorySplit[1];
|
||||||
|
|
||||||
let issueOrPR = getIssueOrPR(parseInt(query), repository);
|
let issueOrPR = await getIssueOrPR(parseInt(query), repository);
|
||||||
if (!issueOrPR) {
|
if (!issueOrPR) {
|
||||||
const res = await fetch(`https://api.github.com/search/issues?q=${encodeURIComponent(query)}${encodeURIComponent(' repo:oven-sh/bun')}`);
|
const res = await fetch(`https://api.github.com/search/issues?q=${encodeURIComponent(query)}${encodeURIComponent(' repo:oven-sh/bun')}`);
|
||||||
|
|
||||||
|
|
17
src/index.ts
17
src/index.ts
|
@ -12,7 +12,7 @@ import { Commands } from './managers/CommandManager';
|
||||||
import registerCommands from './utils/registerCommands';
|
import registerCommands from './utils/registerCommands';
|
||||||
import { Option, OptionOptions } from './structures/Option';
|
import { Option, OptionOptions } from './structures/Option';
|
||||||
import { AutocompleteContext } from './structures/contexts/AutocompleteContext';
|
import { AutocompleteContext } from './structures/contexts/AutocompleteContext';
|
||||||
import { deleteIssue, deletePullRequest, fetchIssues, fetchPullRequests, issues, setIssue, setPullRequest } from './utils/githubUtils';
|
import { deleteIssueOrPR, fetchIssues, fetchPullRequests, setIssue, setPullRequest } from './utils/githubUtils';
|
||||||
import createHmac from 'create-hmac';
|
import createHmac from 'create-hmac';
|
||||||
|
|
||||||
await fetchIssues();
|
await fetchIssues();
|
||||||
|
@ -95,15 +95,14 @@ app.post('/github_webhook', bodyParse(), (c) => {
|
||||||
const issueOrPr = c.req.parsedBody;
|
const issueOrPr = c.req.parsedBody;
|
||||||
if (issueOrPr.action !== 'deleted') {
|
if (issueOrPr.action !== 'deleted') {
|
||||||
if (issueOrPr.issue) {
|
if (issueOrPr.issue) {
|
||||||
console.log('issue');
|
|
||||||
setIssue({
|
setIssue({
|
||||||
id: issueOrPr.issue.number,
|
id: issueOrPr.issue.number,
|
||||||
repository: issueOrPr.issue.repository_url.replace('https://api.github.com/repos/', ''),
|
repository: issueOrPr.issue.repository_url.replace('https://api.github.com/repos/', ''),
|
||||||
title: issueOrPr.issue.title,
|
title: issueOrPr.issue.title,
|
||||||
number: issueOrPr.issue.number,
|
number: issueOrPr.issue.number,
|
||||||
state: issueOrPr.issue.state,
|
state: issueOrPr.issue.state,
|
||||||
created_at: new Date(issueOrPr.issue.created_at),
|
created_at: issueOrPr.issue.created_at,
|
||||||
closed_at: new Date(issueOrPr.issue.closed_at),
|
closed_at: issueOrPr.issue.closed_at,
|
||||||
html_url: issueOrPr.issue.html_url,
|
html_url: issueOrPr.issue.html_url,
|
||||||
user_login: issueOrPr.issue.user.login,
|
user_login: issueOrPr.issue.user.login,
|
||||||
user_html_url: issueOrPr.issue.user.html_url,
|
user_html_url: issueOrPr.issue.user.html_url,
|
||||||
|
@ -117,9 +116,9 @@ app.post('/github_webhook', bodyParse(), (c) => {
|
||||||
title: issueOrPr.pull_request.title,
|
title: issueOrPr.pull_request.title,
|
||||||
number: issueOrPr.pull_request.number,
|
number: issueOrPr.pull_request.number,
|
||||||
state: issueOrPr.pull_request.state,
|
state: issueOrPr.pull_request.state,
|
||||||
created_at: new Date(issueOrPr.pull_request.created_at),
|
created_at: issueOrPr.pull_request.created_at,
|
||||||
closed_at: new Date(issueOrPr.pull_request.closed_at),
|
closed_at: issueOrPr.pull_request.closed_at,
|
||||||
merged_at: new Date(issueOrPr.pull_request.merged_at),
|
merged_at: issueOrPr.pull_request.merged_at,
|
||||||
html_url: issueOrPr.pull_request.html_url,
|
html_url: issueOrPr.pull_request.html_url,
|
||||||
user_login: issueOrPr.pull_request.user.login,
|
user_login: issueOrPr.pull_request.user.login,
|
||||||
user_html_url: issueOrPr.pull_request.user.html_url,
|
user_html_url: issueOrPr.pull_request.user.html_url,
|
||||||
|
@ -127,10 +126,10 @@ app.post('/github_webhook', bodyParse(), (c) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (issueOrPr.issue) deleteIssue(
|
if (issueOrPr.issue) deleteIssueOrPR(
|
||||||
issueOrPr.issue.number, issueOrPr.issue.repository_url.replace('https://api.github.com/repos/', '')
|
issueOrPr.issue.number, issueOrPr.issue.repository_url.replace('https://api.github.com/repos/', '')
|
||||||
);
|
);
|
||||||
else deletePullRequest(
|
else deleteIssueOrPR(
|
||||||
issueOrPr.pull_request.number,
|
issueOrPr.pull_request.number,
|
||||||
issueOrPr.pull_request.html_url
|
issueOrPr.pull_request.html_url
|
||||||
.replace('https://github.com/', '')
|
.replace('https://github.com/', '')
|
||||||
|
|
|
@ -23,7 +23,7 @@ export interface OptionOptions {
|
||||||
maxValue?: number;
|
maxValue?: number;
|
||||||
minLength?: number;
|
minLength?: number;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
run?: (ctx: AutocompleteContext) => Response;
|
run?: (ctx: AutocompleteContext) => Response | Promise<Response>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Option {
|
export class Option {
|
||||||
|
@ -40,7 +40,7 @@ export class Option {
|
||||||
public maxValue?: number;
|
public maxValue?: number;
|
||||||
public minLength?: number;
|
public minLength?: number;
|
||||||
public maxLength?: number;
|
public maxLength?: number;
|
||||||
public run?: (ctx: AutocompleteContext) => Response;
|
public run?: (ctx: AutocompleteContext) => Response | Promise<Response>;
|
||||||
|
|
||||||
public constructor(options: OptionOptions) {
|
public constructor(options: OptionOptions) {
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import utilities from '../../files/utilities.toml';
|
||||||
import MiniSearch from 'minisearch';
|
import MiniSearch from 'minisearch';
|
||||||
import { Logger } from './Logger';
|
import { Logger } from './Logger';
|
||||||
import { APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
|
import { APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
|
||||||
|
import { Database } from 'bun:sqlite';
|
||||||
|
|
||||||
interface Issue {
|
interface Issue {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -12,8 +13,8 @@ interface Issue {
|
||||||
title: string;
|
title: string;
|
||||||
number: number;
|
number: number;
|
||||||
state: 'open' | 'closed',
|
state: 'open' | 'closed',
|
||||||
created_at: Date;
|
created_at: string;
|
||||||
closed_at: Date | null;
|
closed_at: string | null;
|
||||||
html_url: string;
|
html_url: string;
|
||||||
user_login: string;
|
user_login: string;
|
||||||
user_html_url: string;
|
user_html_url: string;
|
||||||
|
@ -21,11 +22,19 @@ interface Issue {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PullRequest extends Issue {
|
interface PullRequest extends Issue {
|
||||||
merged_at: Date | null;
|
merged_at: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let issues: Issue[] = [];
|
export const db = new Database('./files/database.sqlite');
|
||||||
export let pulls: PullRequest[] = [];
|
await db.exec('DROP TABLE IF EXISTS issuesandprs');
|
||||||
|
await db.exec('CREATE TABLE issuesandprs (id INTEGER PRIMARY KEY, repository TEXT, title TEXT, number INTEGER, state TEXT, created_at TEXT, closed_at TEXT, merged_at TEXT, html_url TEXT, user_login TEXT, user_html_url TEXT, type TEXT)');
|
||||||
|
|
||||||
|
const addToDb = db.prepare(
|
||||||
|
'INSERT INTO issuesandprs (repository, title, number, state, created_at, closed_at, merged_at, html_url, user_login, user_html_url, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||||
|
);
|
||||||
|
|
||||||
|
export let issues: number = 0;
|
||||||
|
export let pulls: number = 0;
|
||||||
|
|
||||||
export const fetchIssues = async() => {
|
export const fetchIssues = async() => {
|
||||||
for await (const repository of utilities.github.repositories) {
|
for await (const repository of utilities.github.repositories) {
|
||||||
|
@ -43,22 +52,24 @@ export const fetchIssues = async() => {
|
||||||
for (const issue of res) {
|
for (const issue of res) {
|
||||||
if ('pull_request' in issue) continue;
|
if ('pull_request' in issue) continue;
|
||||||
|
|
||||||
issues.push({
|
// @ts-expect-error it works
|
||||||
id: issue.number,
|
addToDb.run([
|
||||||
repository: issue.repository_url.replace('https://api.github.com/repos/', ''),
|
issue.repository_url.replace('https://api.github.com/repos/', ''),
|
||||||
title: issue.title,
|
issue.title,
|
||||||
number: issue.number,
|
issue.number,
|
||||||
state: issue.state,
|
issue.state,
|
||||||
created_at: new Date(issue.created_at),
|
issue.created_at,
|
||||||
closed_at: new Date(issue.closed_at),
|
issue.closed_at,
|
||||||
html_url: issue.html_url,
|
null,
|
||||||
user_login: issue.user.login,
|
issue.html_url,
|
||||||
user_html_url: issue.user.html_url,
|
issue.user.login,
|
||||||
type: '(ISSUE)'
|
issue.user.html_url,
|
||||||
})
|
'(ISSUE)'
|
||||||
|
]);
|
||||||
|
issues++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug(`Fetching issues for ${repository} - ${issues.length} * ${page}`);
|
Logger.debug(`Fetching issues for ${repository} - ${issues} * ${page}`);
|
||||||
|
|
||||||
page++;
|
page++;
|
||||||
if (res.length === 0) {
|
if (res.length === 0) {
|
||||||
|
@ -66,7 +77,8 @@ export const fetchIssues = async() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.success(`Issues have been fetched for ${repository} - ${issues.length}`);
|
Logger.success(`Issues have been fetched for ${repository} - ${issues}`);
|
||||||
|
issues = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,23 +96,24 @@ export const fetchPullRequests = async() => {
|
||||||
})).json() as any;
|
})).json() as any;
|
||||||
|
|
||||||
for (const pull of res) {
|
for (const pull of res) {
|
||||||
pulls.push({
|
// @ts-expect-error it works
|
||||||
id: pull.number,
|
addToDb.run([
|
||||||
repository: pull.html_url.replace('https://github.com/', '').replace(`/pull/${pull.number}`, ''),
|
pull.html_url.replace('https://github.com/', '').replace(`/pull/${pull.number}`, ''),
|
||||||
title: pull.title,
|
pull.title,
|
||||||
number: pull.number,
|
pull.number,
|
||||||
state: pull.state,
|
pull.state,
|
||||||
created_at: new Date(pull.created_at),
|
pull.created_at,
|
||||||
closed_at: new Date(pull.closed_at),
|
pull.closed_at,
|
||||||
merged_at: new Date(pull.merged_at),
|
pull.merged_at,
|
||||||
html_url: pull.html_url,
|
pull.html_url,
|
||||||
user_login: pull.user.login,
|
pull.user.login,
|
||||||
user_html_url: pull.user.html_url,
|
pull.user.html_url,
|
||||||
type: '(PR)',
|
'(PR)'
|
||||||
})
|
]);
|
||||||
|
pulls++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug(`Fetching pull requests for ${repository} - ${pulls.length} * ${page}`);
|
Logger.debug(`Fetching pull requests for ${repository} - ${pulls} * ${page}`);
|
||||||
|
|
||||||
page++;
|
page++;
|
||||||
if (res.length === 0) {
|
if (res.length === 0) {
|
||||||
|
@ -108,45 +121,71 @@ export const fetchPullRequests = async() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.success(`Pull requests have been fetched for ${repository} - ${pulls.length}`);
|
Logger.success(`Pull requests have been fetched for ${repository} - ${pulls}`);
|
||||||
|
pulls = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setIssue = (issue: Issue) => {
|
export const setIssue = async(issue: Issue) => {
|
||||||
const exists = issues.findIndex(i => i.number === issue.number && i.repository === issue.repository);
|
const exists = await db.prepare(`SELECT * FROM issuesandprs WHERE number = ${issue.number} AND repository = '${issue.repository}'`).get();
|
||||||
if (exists >= 0) issues[exists] = issue;
|
if (typeof exists == 'object') {
|
||||||
else issues.push(issue);
|
db.exec(`UPDATE issuesandprs SET state = '${issue.state}', closed_at = '${issue.closed_at}', title = '${issue.title}' WHERE number = ${issue.number} AND repository = '${issue.repository}'`);
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error
|
||||||
|
addToDb.run([
|
||||||
|
issue.repository.replace('https://api.github.com/repos/', ''),
|
||||||
|
issue.title,
|
||||||
|
issue.number,
|
||||||
|
issue.state,
|
||||||
|
issue.created_at,
|
||||||
|
issue.closed_at,
|
||||||
|
null,
|
||||||
|
issue.html_url,
|
||||||
|
issue.user_login,
|
||||||
|
issue.user_html_url,
|
||||||
|
'(ISSUE)'
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setPullRequest = (pull: PullRequest) => {
|
export const setPullRequest = async(pull: PullRequest) => {
|
||||||
const exists = pulls.findIndex(i => i.number === pull.number);
|
const exists = await db.prepare(`SELECT * FROM issuesandprs WHERE number = ${pull.number} AND repository = '${pull.repository}'`).get();
|
||||||
if (exists >= 0) pulls[exists] = pull;
|
if (typeof exists == 'object') {
|
||||||
else pulls.push(pull);
|
db.exec(`UPDATE issuesandprs SET state = '${pull.state}', closed_at = '${pull.closed_at}', merged_at = '${pull.merged_at}', title = '${pull.title}' WHERE number = ${pull.number} AND repository = '${pull.repository}'`);
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error
|
||||||
|
addToDb.run([
|
||||||
|
pull.repository.replace('https://api.github.com/repos/', ''),
|
||||||
|
pull.title,
|
||||||
|
pull.number,
|
||||||
|
pull.state,
|
||||||
|
pull.created_at,
|
||||||
|
pull.closed_at,
|
||||||
|
pull.merged_at,
|
||||||
|
pull.html_url,
|
||||||
|
pull.user_login,
|
||||||
|
pull.user_html_url,
|
||||||
|
'(ISSUE)'
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteIssue = (number: number, repository: string) => {
|
export const deleteIssueOrPR = (number: number, repository: string) => {
|
||||||
issues = issues.filter(i => i.number === number && i.repository === repository);
|
db.exec(`DELETE FROM issuesandprs WHERE repository = '${repository}' AND number = ${number}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deletePullRequest = (number: number, repository: string) => {
|
export const search = async(query: string, repository: string): Promise<APIApplicationCommandOptionChoice[]> => {
|
||||||
pulls = pulls.filter(p => p.number === number && p.repository === repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const search = (query: string, repository: string): APIApplicationCommandOptionChoice[] => {
|
|
||||||
try {
|
try {
|
||||||
const pullsFiltered = pulls.filter(pull => pull.repository === repository);
|
const arrayFiltered = await db.prepare(`SELECT * FROM issuesandprs WHERE repository = '${repository}'`).all();
|
||||||
const issuesFiltered = issues.filter(issue => issue.repository === repository);
|
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
const array = [].concat(pullsFiltered.slice(0, 13), issuesFiltered.slice(0, 12));
|
const array = arrayFiltered.slice(0, 25);
|
||||||
return array.map((issueOrPr: Issue | PullRequest) => new Object({
|
return array.map((issueOrPr: Issue | PullRequest) => new Object({
|
||||||
name: `${issueOrPr.type} ${issueOrPr.title.slice(0, 93)}`,
|
name: `${issueOrPr.type} ${issueOrPr.title.slice(0, 93).replace(/[^a-z0-9 ]/gi, '')}`,
|
||||||
value: issueOrPr.number.toString()
|
value: issueOrPr.number.toString()
|
||||||
})) as APIApplicationCommandOptionChoice[]
|
})) as APIApplicationCommandOptionChoice[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const array = [].concat(pullsFiltered, issuesFiltered);
|
|
||||||
|
|
||||||
const searcher = new MiniSearch({
|
const searcher = new MiniSearch({
|
||||||
fields: ['title', 'number', 'type'],
|
fields: ['title', 'number', 'type'],
|
||||||
storeFields: ['title', 'number', 'type'],
|
storeFields: ['title', 'number', 'type'],
|
||||||
|
@ -156,7 +195,7 @@ export const search = (query: string, repository: string): APIApplicationCommand
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
searcher.addAll(array);
|
searcher.addAll(arrayFiltered);
|
||||||
|
|
||||||
const result = searcher.search(query);
|
const result = searcher.search(query);
|
||||||
|
|
||||||
|
@ -169,7 +208,7 @@ export const search = (query: string, repository: string): APIApplicationCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getIssueOrPR = (number: number, repository: string): Issue | PullRequest => {
|
export const getIssueOrPR = async(number: number, repository: string): Promise<Issue | PullRequest> => {
|
||||||
return issues.find(issue => issue.number === number && issue.repository === repository) ||
|
const issueOrPR = await db.prepare(`SELECT * FROM issuesandprs WHERE repository = '${repository}' AND number = ${number}`).get();
|
||||||
pulls.find(pull => pull.number === number && pull.repository === repository);
|
return issueOrPR;
|
||||||
}
|
}
|
Loading…
Reference in a new issue