1
- require "tdlib-ruby"
2
1
3
2
namespace :telegram do
4
- desc "Starts the TDLib client to listen for telegram message to automatically collect spam or ham data "
3
+ desc "Starts the TDLib client to listen for telegram messages "
5
4
task listen : :environment do
6
5
unless Rails . env . development?
7
6
puts "TDLib client must only run in development env"
8
7
return
9
8
end
10
9
11
- unless ENV . include? ( "TDLIB_PATH" )
12
- help_message = <<~TEXT
13
- TDLIB_PATH environment variable not found, please build https://github.com/tdlib/td and set environment variable
14
- git clone [email protected] :tdlib/td.git
15
- cd td
16
- mkdir build
17
- cd build
18
- cmake -DCMAKE_BUILD_TYPE=Release ..
19
- cmake --build .
20
- export TDLIB_PATH=$(pwd)
10
+ tdlib_path = ENV [ "TDLIB_PATH" ]
11
+ unless tdlib_path && !tdlib_path . empty?
12
+ puts <<~TEXT
13
+ TDLIB_PATH environment variable not found, please build https://github.com/tdlib/td and set it:
14
+
15
+ git clone https://github.com/tdlib/td.git
16
+ cd td
17
+ mkdir build && cd build
18
+ cmake -DCMAKE_BUILD_TYPE=Release ..
19
+ cmake --build .
20
+ export TDLIB_PATH=$(pwd)
21
21
TEXT
22
- puts help_message
23
- return
22
+ puts "TDLIB_PATH not set. Skipping Telegram listener."
23
+ next
24
24
end
25
25
26
- TD . configure do |config |
27
- config . lib_path = ENV . fetch ( "TDLIB_PATH" )
26
+ require "ffi"
27
+ require "json"
28
+
29
+ # Minimal TDLib FFI wrapper, tdlib-ruby is conflict with
30
+ # telegram-bot-ruby as they depends on dry-core
31
+ module TDJson
32
+ extend FFI ::Library
33
+ lib_name = "tdjson"
34
+ if FFI ::Platform . windows?
35
+ ffi_lib File . join ( ENV . fetch ( "TDLIB_PATH" ) , "#{ lib_name } .dll" )
36
+ elsif FFI ::Platform . mac?
37
+ ffi_lib File . join ( ENV . fetch ( "TDLIB_PATH" ) , "lib#{ lib_name } .dylib" )
38
+ else
39
+ ffi_lib File . join ( ENV . fetch ( "TDLIB_PATH" ) , "lib#{ lib_name } .so" )
40
+ end
41
+
42
+ attach_function :td_json_client_create , [ ] , :pointer
43
+ attach_function :td_json_client_send , [ :pointer , :string ] , :void
44
+ attach_function :td_json_client_receive , [ :pointer , :double ] , :string
45
+ attach_function :td_json_client_execute , [ :pointer , :string ] , :string
46
+ attach_function :td_json_client_destroy , [ :pointer ] , :void
28
47
end
29
48
30
- TD ::Api . set_log_verbosity_level ( 2 )
31
- client = TD ::Client . new
49
+ class TDClient
50
+ def initialize
51
+ @client = TDJson . td_json_client_create
52
+ end
32
53
33
- begin
34
- state = nil
35
- mutex = Mutex . new
36
- cond = ConditionVariable . new
54
+ def send ( query )
55
+ TDJson . td_json_client_send ( @client , JSON . dump ( query ) )
56
+ end
37
57
38
- # Authorization state handler
39
- client . on ( "updateAuthorizationState" ) do | update |
40
- new_state = update . dig ( "authorization_state" , "@type" )
41
- puts "Authorization state: #{ new_state } "
58
+ def receive ( timeout = 1.0 )
59
+ raw = TDJson . td_json_client_receive ( @client , timeout )
60
+ raw && JSON . parse ( raw )
61
+ end
42
62
43
- mutex . synchronize do
44
- state = new_state
45
- cond . signal
63
+ def execute ( query )
64
+ raw = TDJson . td_json_client_execute ( @client , JSON . dump ( query ) )
65
+ raw && JSON . parse ( raw )
46
66
end
47
- end
48
67
49
- # Message Handlers
50
- client . on ( "updateNewMessage" ) do |update |
51
- puts "\n NEW MESSAGE RECEIVED"
52
- message = update [ "message" ]
53
- chat_id = message [ "chat_id" ]
54
- content = message [ "content" ]
55
-
56
- if content [ "@type" ] == "messageText"
57
- message_content = content [ "text" ] [ "text" ]
58
- process_message ( message_content )
59
- puts "Chat ID: #{ chat_id } | Text: #{ message_content } \n "
60
- else
61
- puts "Chat ID: #{ chat_id } | Type: #{ content [ '@type' ] } \n "
68
+ def close
69
+ TDJson . td_json_client_destroy ( @client )
62
70
end
63
- puts "----------------------\n "
64
71
end
65
72
66
- client . on ( "updateMessageContent" ) do |update |
67
- puts "MESSAGE EDITED\n "
68
- chat_id = update [ "chat_id" ]
69
- new_content = update [ "new_content" ]
73
+ client = TDClient . new
70
74
71
- if new_content [ "@type" ] == "messageText"
72
- message_content = new_content [ "text" ] [ "text" ]
73
- process_message ( message_content )
74
- puts "Chat ID: #{ chat_id } | New Text: #{ } \n "
75
- else
76
- puts "Chat ID: #{ chat_id } | New Type: #{ new_content [ '@type' ] } \n "
77
- end
78
- puts "----------------------\n "
79
- end
75
+ # Set log level
76
+ client . send ( { "@type" => "setLogVerbosityLevel" , "new_verbosity_level" => 2 } )
80
77
81
- # Authorization Loop
82
- # https://core.telegram.org/tdlib/getting-started#user-authorization
83
- current_state = nil
84
- mutex . synchronize do
78
+ begin
79
+ state = nil
85
80
loop do
86
- # Wait for state change
87
- cond . wait ( mutex ) while state == current_state
88
- current_state = state
89
- puts "Processing state: #{ current_state } "
81
+ update = client . receive ( 1.0 )
82
+ next unless update
90
83
91
- case current_state
84
+ case update [ "@type" ]
85
+ when "updateAuthorizationState"
86
+ new_state = update [ "authorization_state" ] [ "@type" ]
87
+ puts "Authorization state: #{ new_state } "
88
+ state = new_state
89
+
90
+ case new_state
92
91
when "authorizationStateWaitTdlibParameters"
93
92
puts "Setting TDLib parameters..."
94
- mutex . unlock # Release before network call
95
-
96
93
params = {
97
94
"@type" => "setTdlibParameters" ,
98
95
"api_id" => Rails . application . credentials . dig ( :tdlib_app_id ) ,
@@ -107,45 +104,63 @@ export TDLIB_PATH=$(pwd)
107
104
"device_model" => "Ruby TD Client" ,
108
105
"application_version" => "1.0"
109
106
}
110
- client . broadcast_and_receive ( params )
111
- mutex . lock # Re-acquire after network call
107
+ client . send ( params )
112
108
113
109
when "authorizationStateWaitPhoneNumber"
114
- puts "Please, enter your phone number (e.g., +15551234567):"
115
- mutex . unlock # Release before blocking input
110
+ puts "Please enter your phone number (e.g. +15551234567):"
116
111
phone = STDIN . gets . strip
117
-
118
- params = {
119
- "@type" => "setAuthenticationPhoneNumber" ,
120
- "phone_number" => phone
121
- }
122
- client . broadcast_and_receive ( params )
123
- mutex . lock # Re-acquire
112
+ client . send ( {
113
+ "@type" => "setAuthenticationPhoneNumber" ,
114
+ "phone_number" => phone
115
+ } )
124
116
125
117
when "authorizationStateWaitCode"
126
- puts "Please, enter code from Telegram/SMS:"
127
- mutex . unlock # Release before blocking input
118
+ puts "Please enter the code from Telegram/SMS:"
128
119
code = STDIN . gets . strip
129
-
130
- params = {
131
- "@type" => "checkAuthenticationCode" ,
132
- "code" => code
133
- }
134
- client . broadcast_and_receive ( params )
135
- mutex . lock # Re-acquire
120
+ client . send ( {
121
+ "@type" => "checkAuthenticationCode" ,
122
+ "code" => code
123
+ } )
136
124
137
125
when "authorizationStateReady"
138
126
puts "Authorization successful! Listening for messages..."
139
- # Wait for potential state changes (disconnections, etc.)
140
- cond . wait ( mutex )
141
127
142
128
when "authorizationStateClosed"
143
129
puts "Authorization closed. Exiting."
144
130
break
131
+ else
132
+ puts "Unhandled authorization state: #{ new_state } "
133
+ end
134
+
135
+ when "updateNewMessage"
136
+ message = update [ "message" ]
137
+ chat_id = message [ "chat_id" ]
138
+ content = message [ "content" ]
139
+
140
+ if content [ "@type" ] == "messageText"
141
+ message_content = content [ "text" ] [ "text" ]
142
+ process_message ( message_content )
143
+ puts "Chat ID: #{ chat_id } | Text: #{ message_content } "
144
+ else
145
+ puts "Chat ID: #{ chat_id } | Type: #{ content [ '@type' ] } "
146
+ end
147
+ puts "----------------------"
148
+
149
+ when "updateMessageContent"
150
+ chat_id = update [ "chat_id" ]
151
+ new_content = update [ "new_content" ]
145
152
153
+ if new_content [ "@type" ] == "messageText"
154
+ message_content = new_content [ "text" ] [ "text" ]
155
+ process_message ( message_content )
156
+ puts "Chat ID: #{ chat_id } | New Text: #{ message_content } "
146
157
else
147
- puts "Unhandled authorization state : #{ current_state } "
158
+ puts "Chat ID: #{ chat_id } | New Type : #{ new_content [ '@type' ] } "
148
159
end
160
+ puts "----------------------"
161
+
162
+ else
163
+ # ignore other updates
149
164
end
150
165
end
151
166
0 commit comments