33namespace TheCoder \MonologTelegram ;
44
55use GuzzleHttp \Exception \GuzzleException ;
6- use Illuminate \Routing \Router ;
7- use Illuminate \Support \Facades \Route ;
86use Monolog \Handler \AbstractProcessingHandler ;
97use Monolog \Logger ;
10- use ReflectionMethod ;
11- use TheCoder \MonologTelegram \Attributes \TopicLogInterface ;
128
139class TelegramBotHandler extends AbstractProcessingHandler
1410{
15-
16- protected Router $ router ;
17-
1811 /**
1912 * text parameter in sendMessage method
2013 * @see https://core.telegram.org/bots/api#sendmessage
@@ -25,69 +18,68 @@ class TelegramBotHandler extends AbstractProcessingHandler
2518 * bot api url
2619 * @var string
2720 */
28- protected $ botApi ;
21+ protected string $ botApi ;
2922
3023 /**
3124 * Telegram bot access token provided by BotFather.
3225 * Create telegram bot with https://telegram.me/BotFather and use access token from it.
3326 * @var string
3427 */
35- protected $ token ;
28+ protected string $ token ;
3629
3730 /**
3831 * if telegram is blocked in your region you can use proxy
3932 * @var null
4033 */
41- protected $ proxy ;
34+ protected string | null $ proxy ;
4235
4336 /**
4437 * Telegram channel name.
4538 * Since to start with '@' symbol as prefix.
4639 * @var string
4740 */
48- protected $ chatId ;
41+ protected string | int $ chatId ;
4942
5043 /**
5144 * If chat groups are used instead of telegram channels,
5245 * and the ability to set topics on groups is enabled,
5346 * this configuration can be utilized.
5447 * @var string|null
5548 */
56- protected $ topicId ;
49+ protected string | int | null $ topicId ;
5750
58- protected $ queue = null ;
51+ protected string | null $ queue = null ;
5952
60- protected $ topicsLevel ;
53+ protected TopicDetector $ topicDetector ;
6154
6255 /**
6356 * @param string $token Telegram bot access token provided by BotFather
6457 * @param string $channel Telegram channel name
6558 * @inheritDoc
6659 */
6760 public function __construct (
68- Router $ router ,
69- string $ token ,
70- string $ chat_id ,
71- ?string $ topic_id = null ,
72- ?string $ queue = null ,
73- $ topics_level = [],
74- $ level = Logger::DEBUG ,
75- bool $ bubble = true ,
76- $ bot_api = 'https://api.telegram.org/bot ' ,
77- $ proxy = null )
61+ string $ token ,
62+ string |int $ chat_id ,
63+ string |null $ topic_id = null ,
64+ string |null $ queue = null ,
65+ array $ topics_level = [],
66+ $ level = Logger::DEBUG ,
67+ bool $ bubble = true ,
68+ string $ bot_api = 'https://api.telegram.org/bot ' ,
69+ string |null $ proxy = null )
7870 {
7971 parent ::__construct ($ level , $ bubble );
8072
81- $ this ->router = $ router ;
8273 $ this ->token = $ token ;
8374 $ this ->botApi = $ bot_api ;
8475 $ this ->chatId = $ chat_id ;
8576 $ this ->topicId = $ topic_id ;
8677 $ this ->queue = $ queue ;
87- $ this ->topicsLevel = $ topics_level ;
8878 $ this ->level = $ level ;
8979 $ this ->bubble = $ bubble ;
9080 $ this ->proxy = $ proxy ;
81+
82+ $ this ->topicDetector = new TopicDetector ($ topics_level );
9183 }
9284
9385 /**
@@ -96,7 +88,8 @@ public function __construct(
9688 */
9789 protected function write ($ record ): void
9890 {
99- $ topicId = $ this ->getTopicByAttribute ($ record );
91+ $ topicId = $ this ->topicDetector ->getTopicByAttribute ($ record );
92+
10093 $ token = $ record ['context ' ]['token ' ] ?? null ;
10194 $ chatId = $ record ['context ' ]['chat_id ' ] ?? null ;
10295 $ topicId = $ topicId ?? $ record ['context ' ]['topic_id ' ] ?? null ;
@@ -140,151 +133,4 @@ protected function send(string $message, $token = null, $chatId = null, $topicId
140133 }
141134 }
142135
143- protected function getTopicByAttribute ($ record ): string |null
144- {
145- if (isset ($ record ['context ' ]['exception ' ])) {
146- $ trace = $ record ['context ' ]['exception ' ]->getTrace ();
147-
148- $ commandClass = $ this ->getClassForCommand ($ trace );
149- if ($ commandClass ) {
150- return $ this ->getTopicIdByReflection ($ commandClass , 'handle ' );
151- }
152-
153- $ jobClass = $ this ->getClassForJob ($ trace );
154- if ($ jobClass ) {
155- return $ this ->getTopicIdByReflection ($ jobClass , 'handle ' );
156- }
157- }
158-
159- return $ this ->getTopicByRoute ();
160- }
161-
162- protected function getClassForCommand (array $ trace ): ?string
163- {
164- if (!app ()->runningInConsole ()) {
165- return null ;
166- }
167-
168- foreach ($ trace as $ frame ) {
169- if ($ frame ['function ' ] === 'handle ' && isset ($ frame ['class ' ]) && str_contains ($ frame ['class ' ], 'Console\Commands ' )) {
170- return $ frame ['class ' ];
171- }
172- }
173-
174- return null ;
175- }
176-
177- protected function getClassForJob (array $ trace ): ?string
178- {
179- if (!app ()->bound ('queue.worker ' )) {
180- return null ;
181- }
182-
183- foreach ($ trace as $ frame ) {
184- if ($ frame ['function ' ] === 'handle ' && isset ($ frame ['class ' ]) && str_contains ($ frame ['class ' ], 'App\Jobs ' )) {
185- return $ frame ['class ' ];
186- }
187- }
188-
189- return null ;
190- }
191-
192- protected function getTopicByRoute (): string |null
193- {
194- $ route = Route::current ();
195- if (!$ route || !isset ($ route ->getAction ()['controller ' ])) {
196- return null ;
197- }
198-
199- [$ controller , $ method ] = explode ('@ ' , $ route ->getAction ()['controller ' ]);
200-
201- $ topicId = $ this ->getTopicIdByReflection ($ controller , $ method );
202- if ($ topicId === false ) {
203- $ topicId = $ this ->getTopicIdByRegex ($ route ->getAction ());
204- }
205-
206- return $ topicId ;
207- }
208-
209- protected function getTopicIdByReflection ($ class , $ method ): bool |string |null
210- {
211- try {
212- $ reflectionMethod = new ReflectionMethod ($ class , $ method );
213-
214- $ attributes = $ reflectionMethod ->getAttributes ();
215-
216- if ($ attributes [0 ] !== null ) {
217- /** @var TopicLogInterface $notifyException */
218- $ notifyException = $ attributes [0 ]->newInstance () ?? null ;
219- return $ notifyException ->getTopicId ($ this ->topicsLevel );
220- }
221-
222- } catch (\Throwable $ e ) {
223-
224- }
225- return false ;
226- }
227-
228- protected function getTopicIdByRegex ($ action )
229- {
230- try {
231- // if reflection could not get attribute use regex instead
232- [$ controller , $ method ] = explode ('@ ' , $ action ['controller ' ]);
233-
234- $ filePath = base_path (str_replace ('App ' , 'app ' , $ controller ) . '.php ' );
235- $ fileContent = file_get_contents ($ filePath );
236- $ allAttributes = [];
237-
238- // Regex to match attributes and methods
239- $ regex = '/\#\[\s*(.*?)\s*\]\s*public\s*function\s*(\w+)/ ' ;
240- if (preg_match_all ($ regex , $ fileContent , $ matches , PREG_SET_ORDER )) {
241- foreach ($ matches as $ match ) {
242- $ attributeString = $ match [1 ];
243- $ methodName = $ match [2 ];
244-
245- $ attributes = array_map ('trim ' , explode (', ' , $ attributeString ));
246- foreach ($ attributes as $ attribute ) {
247- $ attributeName = preg_replace ('/\(.*/ ' , '' , $ attribute );
248- $ allAttributes [$ methodName ][] = $ attributeName ;
249- }
250- }
251- }
252-
253- if (empty ($ allAttributes )) {
254- return null ;
255- }
256-
257- if (isset ($ allAttributes [$ method ][0 ])) {
258- foreach ($ this ->topicsLevel as $ key => $ _topicLevel ) {
259- if (str_contains ($ key , $ allAttributes [$ method ][0 ])) {
260- return $ _topicLevel ;
261- }
262- }
263- }
264-
265- } catch (\Throwable $ e ) {
266- }
267- return null ;
268- }
269-
270- public function setToken (string $ token ): static
271- {
272- $ this ->token = $ token ;
273-
274- return $ this ;
275- }
276-
277- public function setChatId (string $ chatId ): static
278- {
279- $ this ->chatId = $ chatId ;
280-
281- return $ this ;
282- }
283-
284- public function setTopicId (string $ topicId ): static
285- {
286- $ this ->topicId = $ topicId ;
287-
288- return $ this ;
289- }
290136}
0 commit comments