11import TelegramBot from "node-telegram-bot-api" ;
22import { Readable , type Stream } from "stream" ;
33import { z } from "zod" ;
4+ import { askCobalt , CobaltResult } from "./cobalt" ;
5+ import { askFxtwitter } from "./fxtwitter" ;
46
57// https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#file-options-metadata
68process . env . NTBA_FIX_350 = "false" ;
@@ -10,67 +12,6 @@ const botParams = {
1012 baseApiUrl : process . env . TELEGRAM_API_URL ,
1113} ;
1214
13- const CobaltResult = z . discriminatedUnion ( "status" , [
14- z . object ( {
15- status : z . literal ( "error" ) ,
16- error : z . object ( {
17- code : z . string ( ) ,
18- context : z
19- . object ( {
20- service : z . string ( ) . optional ( ) ,
21- limit : z . number ( ) . optional ( ) ,
22- } )
23- . optional ( ) ,
24- } ) ,
25- } ) ,
26- z . object ( {
27- status : z . literal ( "picker" ) ,
28- audio : z . string ( ) . optional ( ) ,
29- audioFilename : z . string ( ) . optional ( ) ,
30- picker : z . array (
31- z . object ( {
32- type : z . enum ( [ "photo" , "video" , "gif" ] ) ,
33- url : z . string ( ) ,
34- thumb : z . string ( ) . optional ( ) ,
35- } )
36- ) ,
37- } ) ,
38- z . object ( {
39- status : z . enum ( [ "tunnel" , "redirect" ] ) ,
40- url : z . string ( ) ,
41- filename : z . string ( ) ,
42- } ) ,
43- ] ) ;
44- type CobaltResult = z . infer < typeof CobaltResult > ;
45- type VideoQuality =
46- | "144"
47- | "240"
48- | "360"
49- | "480"
50- | "720"
51- | "1080"
52- | "1440"
53- | "2160"
54- | "4320"
55- | "max" ;
56- async function askCobalt (
57- url : string ,
58- options ?: {
59- videoQuality ?: VideoQuality ;
60- }
61- ) {
62- const response = await fetch ( `https://dorsiblancoapicobalt.nulo.in/` , {
63- method : "POST" ,
64- body : JSON . stringify ( { url, ...options } ) ,
65- headers : {
66- "Content-Type" : "application/json" ,
67- Accept : "application/json" ,
68- } ,
69- } ) ;
70- const data = await response . json ( ) ;
71- return CobaltResult . parse ( data ) ;
72- }
73-
7415class Bot {
7516 private bot : TelegramBot ;
7617 constructor ( token : string ) {
@@ -124,6 +65,49 @@ class Bot {
12465
12566 console . log ( `Descargando ${ parsedUrl . href } ` ) ;
12667
68+ if (
69+ parsedUrl . hostname === "twitter.com" ||
70+ parsedUrl . hostname === "x.com"
71+ ) {
72+ try {
73+ const pathParts = parsedUrl . pathname . split ( "/" ) ;
74+ const statusIndex = pathParts . indexOf ( "status" ) ;
75+ if ( statusIndex !== - 1 && statusIndex + 1 < pathParts . length ) {
76+ const screenName = pathParts [ 1 ] ;
77+ const tweetId = pathParts [ statusIndex + 1 ] ;
78+ const fxResult = await askFxtwitter ( screenName , tweetId ) ;
79+ hasDownloadables = true ;
80+ await this . bot . sendMessage (
81+ chatId ,
82+ `${ fxResult . tweet . author . name } (@${
83+ fxResult . tweet . author . screen_name
84+ } ):\n<blockquote>${ fxResult . tweet . text } ${
85+ fxResult . tweet . quote
86+ ? `</blockquote>\nQuoting: ${ fxResult . tweet . quote . author . name } (@${ fxResult . tweet . quote . author . screen_name } ):\n<blockquote>${ fxResult . tweet . quote . text } `
87+ : ""
88+ } </blockquote>\nhttps://fxtwitter.com/${ screenName } /status/${ tweetId } `,
89+ { reply_to_message_id : msg . message_id , parse_mode : "HTML" }
90+ ) ;
91+ if ( fxResult . tweet . media ?. all ?. length ) {
92+ await this . bot . sendMediaGroup (
93+ chatId ,
94+ fxResult . tweet . media ?. all ?. map ( ( media ) => ( {
95+ type : media . type === "gif" ? "photo" : media . type ,
96+ media : media . url ,
97+ thumb : media . thumbnail_url ,
98+ } ) ) ?? [ ] ,
99+ {
100+ reply_to_message_id : msg . message_id ,
101+ }
102+ ) ;
103+ }
104+ continue ;
105+ }
106+ } catch ( error ) {
107+ console . error ( "Failed to fetch from fxtwitter:" , error ) ;
108+ }
109+ }
110+
127111 const cobaltResult = await askCobalt ( parsedUrl . href ) ;
128112 console . log ( JSON . stringify ( cobaltResult ) ) ;
129113 if ( cobaltResult . status === "error" ) {
0 commit comments