-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* delete old * use legacy * remove deps * telegram start * gg * telegram progress * stream * use channels 0.1.0 * callback query * readme * support sending file * audio video * locations * fix * comment * bring changes * readme * gif * bring changes * fix * fix * better quick_reply * bring changes * revert quick reply change * doc * rewire comment * fix * fix * bring changes * bump * fix merge * add telegram channel to server * fix
- Loading branch information
1 parent
b4dbc00
commit 626ffb9
Showing
25 changed files
with
578 additions
and
20 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './base/channel' | ||
export * from './base/endpoint' | ||
export * from './telegram/channel' |
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,29 @@ | ||
### Sending | ||
|
||
| Channels | Telegram | | ||
| -------- | :------: | | ||
| Text | ✅ | | ||
| Image | ✅ | | ||
| Choice | ✅ | | ||
| Dropdown | ✅ | | ||
| Card | ✅ | | ||
| Carousel | ✅ | | ||
| File | ✅ | | ||
| Audio | ✅ | | ||
| Video | ✅ | | ||
| Location | ✅ | | ||
|
||
### Receiving | ||
|
||
| Channels | Telegram | | ||
| ------------- | :------: | | ||
| Text | ✅ | | ||
| Quick Reply | N/A | | ||
| Postback | ✅ | | ||
| Say Something | ✅ | | ||
| Voice | ❌ | | ||
| Image | ❌ | | ||
| File | ❌ | | ||
| Audio | ❌ | | ||
| Video | ❌ | | ||
| Location | ❌ | |
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,106 @@ | ||
import { Response } from 'express' | ||
import { Context, NarrowedContext } from 'telegraf' | ||
import { Update } from 'telegraf/typings/core/types/typegram' | ||
import yn from 'yn' | ||
import { ChannelApi, ChannelApiManager, ChannelApiRequest } from '../base/api' | ||
import { ChannelInitializeEvent, ChannelStartEvent, ChannelStopEvent } from '../base/service' | ||
import { POSTBACK_PREFIX, SAY_PREFIX } from './renderers/carousel' | ||
import { TelegramService } from './service' | ||
|
||
export class TelegramApi extends ChannelApi<TelegramService> { | ||
async setup(router: ChannelApiManager) { | ||
router.post('/telegram/:token', this.handleRequest.bind(this)) | ||
|
||
this.service.on('start', this.handleStart.bind(this)) | ||
this.service.on('initialize', this.handleInitialize.bind(this)) | ||
this.service.on('stop', this.handleStop.bind(this)) | ||
} | ||
|
||
private async handleRequest(req: ChannelApiRequest, res: Response) { | ||
const { config, callback } = this.service.get(req.scope) | ||
|
||
if (req.params.token === config.botToken) { | ||
req.url = '/' // by-passes verification in telegraf since we do it here instead | ||
callback!(req, res) | ||
} else { | ||
res.sendStatus(401) | ||
} | ||
} | ||
|
||
private async handleInitialize({ scope }: ChannelInitializeEvent) { | ||
if (this.useWebhook()) { | ||
const { telegraf, config } = this.service.get(scope) | ||
const webhook = `${await this.urlCallback!(scope)}/${config.botToken}` | ||
await telegraf.telegram.setWebhook(webhook) | ||
} | ||
} | ||
|
||
private async handleStart({ scope }: ChannelStartEvent) { | ||
const { telegraf } = this.service.get(scope) | ||
|
||
telegraf.on('message', this.asyncCallback(scope, this.handleTelegrafMessage.bind(this))) | ||
telegraf.on('callback_query', this.asyncCallback(scope, this.handleTelegrafCallbackQuery.bind(this))) | ||
|
||
if (this.useWebhook()) { | ||
this.service.get(scope).callback = telegraf.webhookCallback('/') | ||
} else { | ||
await telegraf.telegram.deleteWebhook() | ||
await telegraf.launch() | ||
} | ||
} | ||
|
||
private async handleStop({ scope }: ChannelStopEvent) { | ||
if (!this.useWebhook()) { | ||
this.service.get(scope).telegraf.stop() | ||
} | ||
} | ||
|
||
private async handleTelegrafMessage(scope: string, ctx: NarrowedContext<Context<Update>, Update.MessageUpdate>) { | ||
if ('text' in ctx.message) { | ||
await this.service.receive(scope, this.extractEndpoint(ctx), { type: 'text', text: ctx.message.text }) | ||
} | ||
} | ||
|
||
private async handleTelegrafCallbackQuery( | ||
scope: string, | ||
ctx: NarrowedContext<Context<Update>, Update.CallbackQueryUpdate> | ||
) { | ||
if ('data' in ctx.callbackQuery) { | ||
const data = ctx.callbackQuery.data | ||
|
||
if (data.startsWith(SAY_PREFIX)) { | ||
await this.service.receive(scope, this.extractEndpoint(ctx), { | ||
type: 'say_something', | ||
text: data.replace(SAY_PREFIX, '') | ||
}) | ||
} else if (data.startsWith(POSTBACK_PREFIX)) { | ||
await this.service.receive(scope, this.extractEndpoint(ctx), { | ||
type: 'postback', | ||
payload: data.replace(POSTBACK_PREFIX, '') | ||
}) | ||
} | ||
} | ||
|
||
await ctx.answerCbQuery() | ||
} | ||
|
||
private extractEndpoint(ctx: Context) { | ||
const chatId = ctx.chat?.id | ||
const userId = ctx.from?.id | ||
|
||
return { identity: '*', sender: userId!.toString(), thread: chatId!.toString() } | ||
} | ||
|
||
private asyncCallback(scope: string, fn: (scope: string, ctx: any) => Promise<void>) { | ||
return (ctx: any) => { | ||
fn(scope, ctx).catch((e) => { | ||
this.service.logger?.error('Error occurred in telegram callback', e) | ||
}) | ||
} | ||
} | ||
|
||
private useWebhook() { | ||
// TODO: remove this dependency on server env vars | ||
return !yn(process.env.SPINNED) || yn(process.env.CLUSTER_ENABLED) | ||
} | ||
} |
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,23 @@ | ||
import { ChannelTemplate } from '../base/channel' | ||
import { TelegramApi } from './api' | ||
import { TelegramConfig, TelegramConfigSchema } from './config' | ||
import { TelegramService } from './service' | ||
import { TelegramStream } from './stream' | ||
|
||
export class TelegramChannel extends ChannelTemplate<TelegramConfig, TelegramService, TelegramApi, TelegramStream> { | ||
get meta() { | ||
return { | ||
id: 'e578723f-ab57-463c-bc13-b483db9bf547', | ||
name: 'telegram', | ||
version: '1.0.0', | ||
schema: TelegramConfigSchema, | ||
initiable: true, | ||
lazy: true | ||
} | ||
} | ||
|
||
constructor() { | ||
const service = new TelegramService() | ||
super(service, new TelegramApi(service), new TelegramStream(service)) | ||
} | ||
} |
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 Joi from 'joi' | ||
|
||
export interface TelegramConfig { | ||
botToken: string | ||
} | ||
|
||
export const TelegramConfigSchema = { | ||
botToken: Joi.string().required() | ||
} |
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,23 @@ | ||
import { ChatAction, InputFile } from 'telegraf/typings/core/types/typegram' | ||
import { ChannelContext } from '../base/context' | ||
import { TelegramState } from './service' | ||
|
||
export type TelegramContext = ChannelContext<TelegramState> & { | ||
messages: TelegramMessage[] | ||
} | ||
|
||
export interface TelegramMessage { | ||
text?: string | ||
animation?: string | ||
photo?: InputFile | ||
markdown?: boolean | ||
action?: ChatAction | ||
document?: InputFile | ||
audio?: InputFile | ||
video?: InputFile | ||
location?: { | ||
latitude: number | ||
longitude: number | ||
} | ||
extra?: any | ||
} |
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,13 @@ | ||
import path from 'path' | ||
import { AudioRenderer } from '../../base/renderers/audio' | ||
import { AudioContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramAudioRenderer extends AudioRenderer { | ||
renderAudio(context: TelegramContext, payload: AudioContent) { | ||
context.messages.push({ | ||
document: { url: payload.audio, filename: path.basename(payload.audio) }, | ||
extra: { caption: payload.title } | ||
}) | ||
} | ||
} |
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,51 @@ | ||
import path from 'path' | ||
import { Markup } from 'telegraf' | ||
import { InlineKeyboardButton } from 'telegraf/typings/core/types/typegram' | ||
import { CarouselContext, CarouselRenderer } from '../../base/renderers/carousel' | ||
import { ActionOpenURL, ActionPostback, ActionSaySomething, CardContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
type Context = CarouselContext<TelegramContext> & { | ||
buttons: InlineKeyboardButton[] | ||
} | ||
|
||
export const POSTBACK_PREFIX = 'postback::' | ||
export const SAY_PREFIX = 'say::' | ||
|
||
export class TelegramCarouselRenderer extends CarouselRenderer { | ||
startRenderCard(context: Context, _card: CardContent) { | ||
context.buttons = [] | ||
} | ||
|
||
renderButtonUrl(context: Context, button: ActionOpenURL) { | ||
context.buttons.push(Markup.button.url(button.title, button.url)) | ||
} | ||
|
||
renderButtonPostback(context: Context, button: ActionPostback) { | ||
context.buttons.push(Markup.button.callback(button.title, `${POSTBACK_PREFIX}${button.payload}`)) | ||
} | ||
|
||
renderButtonSay(context: Context, button: ActionSaySomething) { | ||
context.buttons.push(Markup.button.callback(button.title, `${SAY_PREFIX}${button.text}`)) | ||
} | ||
|
||
endRenderCard(context: Context, card: CardContent) { | ||
const text = `*${card.title}*${card.subtitle ? '\n' + card.subtitle : ''}` | ||
|
||
if (card.image) { | ||
context.channel.messages.push({ action: 'upload_photo' }) | ||
context.channel.messages.push({ | ||
photo: { | ||
url: card.image, | ||
filename: path.basename(card.image) | ||
}, | ||
extra: { caption: text, parse_mode: 'Markdown', ...Markup.inlineKeyboard(context.buttons) } | ||
}) | ||
} else { | ||
context.channel.messages.push({ | ||
text, | ||
extra: Markup.inlineKeyboard(context.buttons) | ||
}) | ||
} | ||
} | ||
} |
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,15 @@ | ||
import { Markup } from 'telegraf' | ||
import { ChoicesRenderer } from '../../base/renderers/choices' | ||
import { ChoiceContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramChoicesRenderer extends ChoicesRenderer { | ||
renderChoice(context: TelegramContext, payload: ChoiceContent) { | ||
if (!context.messages.length) { | ||
context.messages.push({}) | ||
} | ||
|
||
const buttons = payload.choices.map((x) => Markup.button.callback(x.title, x.value)) | ||
context.messages[0].extra = Markup.keyboard(buttons).oneTime() | ||
} | ||
} |
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,13 @@ | ||
import path from 'path' | ||
import { FileRenderer } from '../../base/renderers/file' | ||
import { FileContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramFileRenderer extends FileRenderer { | ||
renderFile(context: TelegramContext, payload: FileContent) { | ||
context.messages.push({ | ||
document: { url: payload.file, filename: path.basename(payload.file) }, | ||
extra: { caption: payload.title } | ||
}) | ||
} | ||
} |
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,17 @@ | ||
import path from 'path' | ||
import { ImageRenderer } from '../../base/renderers/image' | ||
import { ImageContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramImageRenderer extends ImageRenderer { | ||
renderImage(context: TelegramContext, payload: ImageContent) { | ||
if (payload.image.toLowerCase().endsWith('.gif')) { | ||
context.messages.push({ animation: payload.image, extra: { caption: payload.title } }) | ||
} else { | ||
context.messages.push({ | ||
photo: { url: payload.image, filename: path.basename(payload.image) }, | ||
extra: { caption: payload.title } | ||
}) | ||
} | ||
} | ||
} |
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,19 @@ | ||
import { TelegramAudioRenderer } from './audio' | ||
import { TelegramCarouselRenderer } from './carousel' | ||
import { TelegramChoicesRenderer } from './choices' | ||
import { TelegramFileRenderer } from './file' | ||
import { TelegramImageRenderer } from './image' | ||
import { TelegramLocationRenderer } from './location' | ||
import { TelegramTextRenderer } from './text' | ||
import { TelegramVideoRenderer } from './video' | ||
|
||
export const TelegramRenderers = [ | ||
new TelegramTextRenderer(), | ||
new TelegramImageRenderer(), | ||
new TelegramCarouselRenderer(), | ||
new TelegramChoicesRenderer(), | ||
new TelegramFileRenderer(), | ||
new TelegramAudioRenderer(), | ||
new TelegramVideoRenderer(), | ||
new TelegramLocationRenderer() | ||
] |
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,22 @@ | ||
import { LocationRenderer } from '../../base/renderers/location' | ||
import { LocationContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramLocationRenderer extends LocationRenderer { | ||
renderLocation(context: TelegramContext, payload: LocationContent) { | ||
context.messages.push({ | ||
location: { latitude: payload.latitude, longitude: payload.longitude } | ||
// For some reason this does not work, so we need to send a seperate text message | ||
// extra: { caption: payload.title } | ||
}) | ||
|
||
let text = payload.title | ||
if (payload.address) { | ||
text = (text ? `*${text}*\n` : '') + payload.address | ||
} | ||
|
||
if (payload.title) { | ||
context.messages.push({ text, extra: { parse_mode: 'Markdown' } }) | ||
} | ||
} | ||
} |
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,13 @@ | ||
import { TextRenderer } from '../../base/renderers/text' | ||
import { TextContent } from '../../content/types' | ||
import { TelegramContext } from '../context' | ||
|
||
export class TelegramTextRenderer extends TextRenderer { | ||
renderText(context: TelegramContext, payload: TextContent) { | ||
context.messages.push({ | ||
text: payload.text, | ||
markdown: payload.markdown, | ||
extra: payload.markdown ? { parse_mode: 'Markdown' } : {} | ||
}) | ||
} | ||
} |
Oops, something went wrong.