-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: some improves * build; add nodemon and remove y18n * chre: remove locales * feat: adding whisper transcription * refactor: moving code into controller * refactor: moving code * refactor: extracting types * feature: add middleware to let pass only authorized channels * refactor: some improves * refactor: min improves * chore: dummy change * a try * revert * trying something * trying++ * trying+2 * ?? * trying bla * bla2
- Loading branch information
Showing
27 changed files
with
318 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { BaseContext } from './types'; | ||
|
||
export default class BaseController { | ||
static showTypingAction(ctx: BaseContext) { | ||
ctx.telegram.sendChatAction(ctx.chat.id, 'typing'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { ChatCompletionMessageParam } from 'openai/resources'; | ||
import { Message } from 'telegraf/typings/core/types/typegram'; | ||
|
||
import { TextMatchedContext, TranscriptContext } from './types'; | ||
import { espiId } from '../../config'; | ||
import GPT from '../../core/GPT'; | ||
import api from '../../lib/api'; | ||
import BaseController from '../BaseController'; | ||
|
||
export default class GPTController extends BaseController { | ||
private static maxAudioSize = 10 * 1024 * 1024; // 10MB | ||
|
||
/** | ||
* Handle incoming question asking to ChatGPT API | ||
*/ | ||
static async handleQuestion(ctx: TextMatchedContext) { | ||
this.showTypingAction(ctx); | ||
const question = (ctx.match?.groups?.question || '').trim(); | ||
if (question.length < 7) { | ||
ctx.reply('Muy cortito amigo'); | ||
return; | ||
} | ||
|
||
const reply = ctx.message.reply_to_message as Message.TextMessage; | ||
const contextText = reply?.text || ''; | ||
const contextRole = reply?.from?.id === espiId ? 'system' : 'user'; | ||
const context: ChatCompletionMessageParam | undefined = contextText | ||
? { role: contextRole, content: contextText } | ||
: undefined; | ||
const answer = await GPT.ask(question, context); | ||
ctx.reply(answer); | ||
} | ||
|
||
/** | ||
* Transcript audio using ChatGPT API | ||
*/ | ||
static async transcriptAudio(ctx: TranscriptContext) { | ||
this.showTypingAction(ctx); | ||
const voice = ctx.message?.voice; | ||
const link = await ctx.telegram.getFileLink(voice.file_id); | ||
const { data: buffer } = await api.get<Uint8Array>(link.href, { responseType: 'arraybuffer' }); | ||
|
||
// If audio is too big, don't transcribe it and return | ||
if (buffer.length > this.maxAudioSize) { | ||
ctx.reply('El audio es muy grande, no puedo transcribirlo'); | ||
return; | ||
} | ||
|
||
// Transcript audio and reply with it | ||
const transcription = await GPT.transcript(buffer, ctx.message?.message_id); | ||
ctx.reply(transcription); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Context, NarrowedContext } from 'telegraf'; | ||
import { Message, Update } from 'telegraf/typings/core/types/typegram'; | ||
|
||
export type TextMatchedContext = NarrowedContext< | ||
Context & { match: RegExpExecArray }, | ||
Update.MessageUpdate<Message.TextMessage> | ||
>; | ||
|
||
export type TranscriptContext = NarrowedContext<Context, Update.MessageUpdate<Message.VoiceMessage>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,28 @@ | ||
import { Context } from 'telegraf'; | ||
import { Update } from 'telegraf/typings/core/types/typegram'; | ||
|
||
import stickers from '../features/stickers'; | ||
import BaseController from './BaseController'; | ||
import stickers from '../core/stickers'; | ||
|
||
const StickersController = { | ||
replyWithSticker: async (ctx: Context<Update>, stickerPromise: Promise<string>) => { | ||
export default class StickersController extends BaseController { | ||
static async replyWithSticker(ctx: Context<Update>, stickerPromise: Promise<string>) { | ||
const sticker = await stickerPromise; | ||
if (!sticker) { | ||
await ctx.reply('Acá debería venir un sticker pero no lo encontré :('); | ||
} | ||
|
||
await ctx.replyWithSticker(sticker); | ||
}, | ||
} | ||
|
||
replyWithMaybeFacu: async (ctx: Context<Update>) => { | ||
static async replyWithMaybeFacu(ctx: Context<Update>) { | ||
await StickersController.replyWithSticker(ctx, stickers.maybeFacu()); | ||
}, | ||
} | ||
|
||
replyWithPaintedDog: async (ctx: Context<Update>) => { | ||
static async replyWithPaintedDog(ctx: Context<Update>) { | ||
await StickersController.replyWithSticker(ctx, stickers.paintedDog()); | ||
}, | ||
} | ||
|
||
replyWithPatternMatchingDan: async (ctx: Context<Update>) => { | ||
static async replyWithPatternMatchingDan(ctx: Context<Update>) { | ||
await StickersController.replyWithSticker(ctx, stickers.patternMatchingDan()); | ||
}, | ||
}; | ||
|
||
export default StickersController; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { Context, NarrowedContext } from 'telegraf'; | ||
import { Update } from 'telegraf/typings/core/types/typegram'; | ||
|
||
export type BaseContext = NarrowedContext<Context, Update.MessageUpdate>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import fs from 'fs'; | ||
|
||
import OpenAI from 'openai'; | ||
import { ChatCompletionMessageParam } from 'openai/resources'; | ||
|
||
import { openAI } from '../config'; | ||
import logger from '../lib/logger'; | ||
|
||
/** | ||
* Wrapper for OpenAI API | ||
* @see https://platform.openai.com/docs/api-reference | ||
*/ | ||
const openai = new OpenAI({ apiKey: openAI.apiKey }); | ||
|
||
/** | ||
* Wrapper class for ChatGPT API | ||
*/ | ||
export default class GPT { | ||
/** | ||
* Ask a question to ChatGPT API | ||
* @param question Question to ask | ||
* @param context Context to provide to the API | ||
* @returns Answer from the API | ||
*/ | ||
static async ask(question: string, context?: ChatCompletionMessageParam) { | ||
const messages: ChatCompletionMessageParam[] = [ | ||
{ role: 'system', content: 'Sus un bot de Telegram llamado Espi. Estás en el grupo "12 Cactus".' }, | ||
]; | ||
if (context) messages.push(context); | ||
messages.push({ role: 'user', content: question }); | ||
|
||
const response = await openai.chat.completions.create({ | ||
...openAI.defaultOptions, | ||
messages, | ||
}); | ||
|
||
const answer = response.choices[0].message?.content || ''; | ||
return answer || 'Ups... no se que decirte'; | ||
} | ||
|
||
/** | ||
* Transcribe an audio file | ||
* @param buffer Audio file buffer | ||
* @param messageId Message ID to use as filename | ||
* @returns Transcription | ||
*/ | ||
static async transcript(buffer: Uint8Array, messageId = 42) { | ||
const filename = `./voice-${messageId}.mp3`; | ||
try { | ||
await fs.promises.writeFile(filename, buffer); | ||
const file = fs.createReadStream(filename); | ||
const transcript = await openai.audio.transcriptions.create({ | ||
file, | ||
model: 'whisper-1', | ||
language: 'es', | ||
}); | ||
return transcript.text; | ||
} catch (error: any) { | ||
logger.error(error, { filename }); | ||
return 'Hubo un problema desconocido al transcribir el audio...'; | ||
} finally { | ||
await fs.promises.rm(filename, { force: true }); | ||
} | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.