Compare commits

...

68 Commits

Author SHA1 Message Date
eleksir
65a3382bb0 Do not manipulate lib path directly.
Perl is a bit fucky about addng paths to @INC directly or via use lib;
Also update .gitignore.
2026-01-02 16:16:22 +03:00
eleksir
8f6ccfba04 Add export_settings.pl to save chat settings in json
Add missing functions to lib/BotLib/Admin.pm to be able to expoprt
chat settings for for particular chats.
Add extract_chatlist.txt.pl to extract chat list from debug.log
2026-01-02 15:56:08 +03:00
eleksir
2d0ad8560d Enhance logging, disable autoban spammers 2025-05-19 22:08:26 +03:00
eleksir
c24644b10e Ban spammers in slackware_ru chat automatically 2025-05-10 00:44:31 +03:00
eleksir
f97557868b User sendMessage directly via global object, without sugar 2025-05-09 16:19:49 +03:00
eleksir
b024d5f13a Do not send "Typing" as it cause errors, replace it with sleep. 2025-05-09 11:59:18 +03:00
eleksir
bc270457a4 Use correct fields for last_name and user id in updated member event (when new user joins) 2025-05-08 21:16:29 +03:00
eleksir
ba4ff36daf Solve myterious riddle with users some sneaking unnoted by bot, which is a bummer! 2025-05-08 17:51:41 +03:00
eleksir
ec59703000 Fix reaction username in logs 2025-05-08 17:27:52 +03:00
eleksir
59a1a3538b I have nothing to do with chat reactions, so log it and skip 2025-05-08 17:20:50 +03:00
eleksir
6e76d85899 Add quirk for api incosistency in reactions 2025-05-08 17:11:16 +03:00
eleksir
d21bab68ef Make proper use of Reaction stuff in Teapot lib 2025-05-08 15:49:25 +03:00
eleksir
408dccd1b5 Fix variable name in Brain.pm that explodes bot 2025-05-08 15:06:12 +03:00
eleksir
a568c6f146 Do not include in repo data/Admin directory 2025-05-08 15:03:45 +03:00
eleksir
5448c9bfdb Add type Reaction 2025-05-08 14:56:25 +03:00
eleksir
da182ae62f Do not call logOut() since we have no webhooks and for faster restarts. 2025-04-27 13:07:15 +03:00
eleksir
82df563f40 Add list of allowed updates explicitly, add Close() method for telegram connection 2025-04-27 13:05:31 +03:00
eleksir
7bd0c3428f Set explicitly ["chat_member","message_reaction","message_reaction_count"] as additional updates i want to receive 2025-04-24 22:01:29 +03:00
eleksir
7904957aaa Implement commands w п weather погода погодка погадка without arguments 2025-02-09 14:51:10 +03:00
eleksir
132ebeffa4 Fix crash on condition check in !admin {obutts,oboobs} # command 2024-10-25 09:44:37 +03:00
eleksir
4f9675f1d2 Import JSON::PP explicitly in lib/BotLib.pm 2024-08-22 21:44:09 +03:00
eleksir
1667772268 Make use of logOut() method 2024-08-22 21:42:58 +03:00
eleksir
ebaf2c9b38 Add logOut() method to Teapot lib 2024-08-22 21:29:49 +03:00
eleksir
b9c88ad4e7 Dump SendMessage() error only if we not handle it properly 2023-08-29 17:28:35 -07:00
eleksir
6a4c2bd076 Add command пословиться 2023-05-19 00:16:14 +03:00
eleksir
2f9b79d77d Add missing semicolon in Teapot/Bot/Object/Chat.pm 2023-04-16 17:06:33 +03:00
eleksir
8eb83ac2b8 Update internal version number of Teapot lib 2023-04-16 17:03:39 +03:00
eleksir
df3c198bce Add general support for BotAPI v6.6 2023-04-16 16:58:59 +03:00
eleksir
f019b98b1f Add partial BotAPI v6.5 support 2023-04-16 16:31:12 +03:00
eleksir
ecf4f94dca Add general support for BotAPI v6.4 2023-04-16 16:03:38 +03:00
eleksir
cea6920d78 Change loglevel for known errors to notice while handling sendMessage errors 2023-01-14 20:52:03 +03:00
eleksir
d4d830715b Add comments fo couple of fields in banChatMember method 2023-01-14 20:50:02 +03:00
eleksir
be5c654045 Rework BotAPI error handling, this time it looks kinda working, still wip, though 2023-01-07 17:19:16 +03:00
eleksir
5edf5a7e0c Fine tune GET polling requests timings, update Teapot lib pod-docs, bump Teapot lib version where missed last time 2023-01-07 16:56:56 +03:00
eleksir
780ec4fe29 Correctly handle successful response from banChatMember and restrictChatMember in BotLib.pm 2023-01-07 16:53:47 +03:00
eleksir
fc56f99433 Try to catch and log as most Bot API errors as we can in BotLib.pm 2023-01-02 21:27:34 +03:00
eleksir
5b93e43280 Only creator can toggle admin mute 2023-01-02 19:54:22 +03:00
eleksir
c5b4d1c866 Try to catch and log as most Bot API errors as we can in TelegramBot.pm 2023-01-02 19:34:38 +03:00
eleksir
5a5e4b5ead 5/5 Fix formatting in TelegramBot.pm 2023-01-02 19:21:19 +03:00
eleksir
e8765c0e88 4/5 Try to handle errors on sending message in RedisLib.pm 2023-01-02 19:20:31 +03:00
eleksir
3dc75936ea 3/5 Allow to enable mute-by-command not only to creator of chat 2023-01-02 19:08:02 +03:00
eleksir
1f3675de8f 2/5 Lift up error to caller in Teapot lib, introduce can_talk() in Brain.pm 2023-01-02 19:06:02 +03:00
eleksir
3668620bd8 1/5 Fix formatting in bin/aleesa-telegram 2023-01-02 19:04:28 +03:00
eleksir
5411d6df7f Fix fetching error message 2022-12-15 02:21:17 +03:00
eleksir
f6a5a05e58 Disable chat in morning greet list if chat id is not found 2022-12-15 02:19:01 +03:00
eleksir
6e084995a7 Use proper functions to check state of services in migration 2022-12-15 02:03:02 +03:00
eleksir
43c08e156f Return only if migration performed 2022-12-12 17:31:18 +03:00
eleksir
b84f8bb245 Move IsCensored to Admin.pm 2022-12-12 17:26:50 +03:00
eleksir
d43cb5995b Add settings migration when group -> supergroup promotion event discovered 2022-12-12 17:10:02 +03:00
eleksir
7d74a86dad In channel context Hightlight() is pointless 2022-12-12 15:19:41 +03:00
eleksir
d539c8c9cf DeeJay utility functions to Util.pm 2022-12-12 13:32:44 +03:00
eleksir
bd9f66930b In _post_request() try to catch error message as raw and return it to upper layer 2022-12-12 12:41:54 +03:00
eleksir
9618369a45 Add debug for api errors 2022-11-26 14:22:11 +03:00
eleksir
770fce4cfe Update dependencies 2022-11-26 14:16:19 +03:00
eleksir
80d9aeca20 Add fuck command 2022-11-26 14:02:52 +03:00
eleksir
548c0b3655 Fix logic in GreetMsgToggle() 2022-11-07 00:57:09 +03:00
eleksir
b4e6685213 Add support for Bot API 6.3 2022-11-06 22:08:02 +03:00
eleksir
f9658ac5e8 Actually add admin mute command 2022-10-09 02:06:54 +03:00
eleksir
3a75f4c947 Rename admin mute command because it actually bans chat prticipants 2022-10-09 00:47:38 +03:00
eleksir
58128bdbdb Add utility script 2022-05-13 18:06:21 +03:00
eleksir
bec963c7b3 Allow to parse any json-file as loosely formatted (not only config.json) 2022-05-13 18:05:15 +03:00
eleksir
b2af26d4e6 Ratelimit good morning greet 2022-05-13 11:50:41 +03:00
eleksir
2708d84d8a Properly set loglevel if it omitted in config 2022-05-13 11:49:22 +03:00
eleksir
508b749dff Fix 'open' module version, it is hardwired to perl distribution 2022-05-13 11:47:02 +03:00
eleksir
cefdec95c4 Forgot to sed /voice/video/g in message.pm 2022-05-01 11:50:00 +03:00
eleksir
35104f883c Take into account possible api errors 2022-04-24 01:13:36 +03:00
eleksir
281a5e6b3c Remove excessive debug, fix message format
* Remove duplicate debug for outgoing redis message
* Add log category for outgoing debug redis message
* Add missing field in outgoing redis message
2022-04-24 01:00:06 +03:00
eleksir
060244a84d Cleanup and update sample config 2022-04-24 00:54:23 +03:00
70 changed files with 2803 additions and 820 deletions

2
.gitignore vendored
View File

@@ -4,5 +4,7 @@ _Deparsed_XSubs.pm
.vstags
aleesa-telegram.pid
data/cache/**
data/admin/**
data/config.json
data/chatlist.txt
vendor_perl/**

View File

@@ -4,7 +4,7 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use strict;
use warnings;
use utf8;
use open qw (:std :utf8);
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use version; our $VERSION = qw (1.0);
@@ -24,29 +24,33 @@ BEGIN {
chdir $workdir;
}
use lib ("$workdir/lib", "$workdir/vendor_perl", "$workdir/vendor_perl/lib/perl5");
use lib ("$workdir/lib", "$workdir/vendor_perl", "$workdir/vendor_perl/lib/perl5");
no Cwd;
use Cwd::utf8 qw (chdir abs_path);
use File::Basename qw (basename dirname);
use File::Path qw (make_path);
use File::Spec qw ();
use Log::Any qw ($log);
use Log::Any::Adapter ();
use Cwd::utf8 qw (chdir abs_path);
use File::Basename qw (basename dirname);
use File::Path qw (make_path);
use File::Spec qw ();
use Log::Any qw ($log);
use Log::Any::Adapter ();
use Log::Any::For::Std ();
use POSIX qw (setgid setuid setsid);
use BotLib::Conf qw (LoadConf);
use TelegramBot qw (RunTelegramBot);
use POSIX qw (setgid setuid setsid);
use BotLib::Conf qw (LoadConf);
use TelegramBot qw (RunTelegramBot);
sub daemonize;
sub __fork;
sub __signal_handler;
local $OUTPUT_AUTOFLUSH = 1;
my $runAsUid = $UID;
my $piddir = '/var/run/' . basename (abs_path ($PROGRAM_NAME));
my $C = LoadConf ();
my $loglevel = $C->{'loglevel'} // 'notice';
my $piddir = '/var/run/' . basename (abs_path ($PROGRAM_NAME));
my $C = LoadConf ();
my $loglevel = 'notice';
if (defined $C->{'loglevel'}) {
$loglevel = $C->{'loglevel'};
}
my $logfile;
if (defined $C->{'log'}) {

View File

@@ -7,7 +7,7 @@ requires 'Cwd', '==3.75';
requires 'Cwd::utf8', '==0.011';
requires 'Data::Dumper', '==2.183';
requires 'Digest::SHA', '==6.02';
requires 'Encode', '==3.16';
requires 'Encode', '==3.19';
requires 'English', '0';
requires 'Exporter', '==5.74';
# part of perl distribution
@@ -15,32 +15,33 @@ requires 'IPC::Open2', '0';
requires 'File::Basename', '==2.85';
requires 'File::Path', '==2.18';
requires 'File::Spec', '==3.75';
requires 'IO::Socket::SSL', '==2.074';
requires 'JSON', '==4.05';
requires 'JSON::PP', '==4.07';
requires 'IO::Socket::SSL', '==2.077';
requires 'JSON', '==4.10';
requires 'JSON::PP', '==4.12';
requires 'JSON::XS', '==4.03';
requires 'List::Util', '==1.60';
requires 'local::lib', '==2.000028';
requires 'Log::Any', '==1.710';
requires 'Log::Any::Adapter', '==1.710';
requires 'local::lib', '==2.000029';
requires 'Log::Any', '==1.711';
requires 'Log::Any::Adapter', '==1.711';
requires 'Log::Any::For::Std', '==0.05';
requires 'Math::Random::Secure', '==0.080001';
requires 'MIME::Base64', '==3.16';
requires 'Mojolicious', '==9.24';
requires 'Mojolicious', '==9.30';
# part of Mojolicious
requires 'Mojo::Base', '0';
# part of Mojolicious
requires 'Mojo::IOLoop', '0';
requires 'Mojo::Redis', '==3.27';
requires 'Mojo::Redis', '==3.29';
# part of Mojo::Redis
requires 'Mojo::Redis::Connection', '0';
# part of Mojolicious
requires 'Mojo::UserAgent', '0';
requires 'open', '==1.12';
# part of perl distribution
requires 'open', '0';
# part of perl distribution
requires 'POSIX', '0';
requires 'Text::Fuzzy', '==0.29';
requires 'URI::URL', '==5.10';
requires 'URI::URL', '==5.17';
# part of perl distribution
requires 'utf8', '0';
requires 'version', '==0.9929';

View File

@@ -2,13 +2,8 @@
# Cachedir used not only for caches but for some settings and stats too.
"cachedir" : "data/cache",
"gamestat" : {
"dir" : "data",
"gamestatdir" : "data/gamestat"
},
# Required for debugging reasons, if commented out or not exist, no debug logging appears at all.
"log" : "/var/log/aleesa-telegram-bot/aleesa.log",
"log" : "/var/log/aleesa-telegram/debug.log",
# Verbosity can be trace, debug, info (inform), notice, warning (warn), error (err), critical (crit, fatal), alert, emergency.
# Currently implemented debug, info, notice, warn, error, fatal. Notice is default logging level.
@@ -16,10 +11,6 @@
# Stuff for telegram itself.
"telegrambot" : {
# Directory with per-conversation "brains".
"braindir" : "data/telegrambot-brains",
# Prefix-sign for all commands. In many irc-bots (eggdrop/windrop) it is "." (dot), in sulci it was "!".
# In telegram commands begin with "/" on the start of first (and only) line. In case of this bot you can define what to use.
"csign" : "!",
@@ -30,10 +21,11 @@
# Unprivileged user to run this bot from.
"user" : "nobody",
"redis_server": "127.0.0.1",
"redis_server": "redis",
"redis_port" : "6379",
"redis_channels" : [
"telegram"
],
"redis_router_channel" : "misc"
}

168
export_settings.pl Normal file
View File

@@ -0,0 +1,168 @@
#!/usr/bin/perl
# Плагин для сохранения настроек чатов. Экспортирует настройки в json, который можно использовать для импорта в другого
# бота.
#
# Файлик data/chatlist.txt придётся добывать грепаньем дебаг-логов (через скрипт extract_chatlist.txt.pl).
# Формат 1 chatid на строку.
use 5.018; ## no critic (ProhibitImplicitImport)
use strict;
use warnings;
use utf8;
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use version; our $VERSION = qw (1.0);
my $workdir;
# before we run, change working dir
BEGIN {
use Cwd qw (chdir abs_path);
my @CWD = split /\//xms, abs_path ($PROGRAM_NAME);
if ($#CWD > 1) {
$#CWD = $#CWD - 1;
}
$workdir = join '/', @CWD;
chdir $workdir;
}
use lib ("$workdir/lib", "$workdir/vendor_perl", "$workdir/vendor_perl/lib/perl5");
no Cwd;
use Data::Dumper qw(Dumper);
use JSON::XS ();
use BotLib::Admin qw(
FortuneToggleList FortuneEnabled
ChanMsgToggleList ChanMsgEnabled
GreetMsgToggleList GreetMsgEnabled
GoodbyeMsgToggleList GoodbyeMsgEnabled
MuteByAdminToggleList MuteByAdminEnabled
PluginEnabled);
use BotLib::Conf qw(LoadConf);
my $settings;
my $chatlistfile = 'data/chatlist.txt';
my $c = LoadConf('data/config.json');
my $admin_dir = $c->{admin}->{dir};
my $cachedir = $c->{cachedir};
# Достаём список чатов, где настроена fortune-ка по утрам.
my @fortune_chats = FortuneToggleList();
foreach my $chatid (@fortune_chats) {
$settings->{$chatid}->{fortune} = FortuneEnabled($chatid);
}
# Достаём список чатов, где настроена возможность удаления сообщений, отправленных от имени другого калана.
my @chanmsg_chats = ChanMsgToggleList();
foreach my $chatid (@chanmsg_chats) {
$settings->{$chatid}->{chanmsg} = ChanMsgEnabled($chatid);
}
# Достаём список чатов, где настроены приветственные сообщения.
my @greetmsg_chats = GreetMsgToggleList();
foreach my $chatid (@greetmsg_chats) {
$settings->{$chatid}->{greetmsg} = GreetMsgEnabled($chatid);
}
# Достаём список чатов, где настроены прощальные сообщения.
my @goodbyemsg_chats = GoodbyeMsgToggleList();
foreach my $chatid (@goodbyemsg_chats) {
$settings->{$chatid}->{goodbyemsg} = GoodbyeMsgEnabled($chatid);
}
# Достаём список чатов, где настроена возможность мута администратором.
my @adminmute_chats = MuteByAdminToggleList();
foreach my $chatid (@adminmute_chats) {
$settings->{$chatid}->{adminmute} = MuteByAdminEnabled($chatid);
}
open my $F, '<', $chatlistfile or die "Unable to open $chatlistfile\n"; ## no critic (ErrorHandling::RequireUseOfExceptions,InputOutput::RequireBriefOpen)
foreach my $chatid (<$F>) { ## no critic (InputOutput::ProhibitReadlineInForLoop)
chomp $chatid;
next if $chatid eq '';
if (PluginEnabled($chatid, 'oboobs')) {
$settings->{$chatid}->{plugins}->{oboobs} = 1;
}
if (PluginEnabled($chatid, 'obutts')) {
$settings->{$chatid}->{plugins}->{obutts} = 1;
}
}
close $F;
my $data;
foreach my $chatid (keys %$settings) {
my $item->{id} = $chatid;
if ($settings->{$chatid}->{fortune}) {
$item->{fortune} = 1;
} else {
$item->{fortune} = 0;
}
if ($settings->{$chatid}->{chanmsg}) {
$item->{chanmsg} = 1;
} else {
$item->{chanmsg} = 0;
}
if ($settings->{$chatid}->{greetmsg}) {
$item->{greetmsg} = 1;
} else {
$item->{greetmsg} = 0;
}
if ($settings->{$chatid}->{goodbyemsg}) {
$item->{goodbyemsg} = 1;
} else {
$item->{goodbyemsg} = 0;
}
if ($settings->{$chatid}->{adminmute}) {
$item->{adminmute} = 1;
} else {
$item->{adminmute} = 0;
}
if ($settings->{$chatid}->{plugins}->{obutts}) {
$item->{plugins}->{obutts} = 1;
} else {
$item->{plugins}->{obutts} = 0;
}
if ($settings->{$chatid}->{plugins}->{oboobs}) {
$item->{plugins}->{oboobs} = 1;
} else {
$item->{plugins}->{oboobs} = 0;
}
push @{$data}, $item;
}
my $json = eval { JSON::XS->new->utf8(1)->pretty(1)->encode($data) };
if (defined $json) {
print $json . "\n";
} else {
die "Unable to generate json, something went wrong: $EVAL_ERROR\n"; ## no critic (ErrorHandling::RequireUseOfExceptions)
}
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:

28
extract_chatlist.txt.pl Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/perl
#
use strict;
use warnings;
my %chats;
open my $F, '<', 'debug.log' or die "Unable to open debug.log: $!\n"; ## no critic (ErrorHandling::RequireUseOfExceptions, InputOutput::RequireBriefOpen)
foreach my $line (<$F>) { ## no critic (InputOutput::ProhibitReadlineInForLoop)
if ($line =~ /.DEBUG. In public chat .* \(\-(\d+)\) .* say:/) {
next unless defined $1;
next if $1 eq '';
$chats{$1} = 1;
}
}
close $F;
open $F, '>', 'chatlist.txt' or die "Unable to open chatlist.txt: $!\n"; ## no critic (ErrorHandling::RequireUseOfExceptions)
foreach my $chat (keys %chats) {
print $F "$chat\n";
}
close $F;
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:

View File

@@ -10,6 +10,7 @@ use English qw ( -no_match_vars );
use Clone qw (clone);
use Data::Dumper qw (Dumper);
use JSON::PP ();
use Log::Any qw ($log);
use Math::Random::Secure qw (irand);
@@ -17,22 +18,25 @@ use BotLib::Conf qw (LoadConf);
use BotLib::Admin qw (@ForbiddenMessageTypes GetForbiddenTypes AddForbiddenType
DelForbiddenType ListForbidden FortuneToggle FortuneStatus
PluginToggle PluginStatus PluginEnabled ChanMsgToggle ChanMsgStatus
GreetMsgToggle GreetMsgStatus GoodbyeMsgToggle GoodbyeMsgStatus);
use BotLib::Util qw (trim);
GreetMsgToggle GreetMsgStatus GoodbyeMsgToggle GoodbyeMsgStatus MuteByAdminToggle
MuteByAdminStatus MuteByAdminEnabled);
use BotLib::Util qw (trim Highlight);
use version; our $VERSION = qw (1.1);
use Exporter qw (import);
our @EXPORT_OK = qw (Command Highlight BotSleep IsCensored);
our @EXPORT_OK = qw (Command);
my $c = LoadConf ();
my $csign = $c->{telegrambot}->{csign};
my $redismsg->{from} = 'telegram';
$redismsg->{plugin} = 'telegram';
$redismsg->{misc}->{answer} = 1;
$redismsg->{misc}->{csign} = "$c->{telegrambot}->{csign}";
$redismsg->{misc}->{msg_format} = 0;
$redismsg->{misc}->{fwd_cnt} = 1;
my $redismsg->{from} = 'telegram';
$redismsg->{threadid} = '';
$redismsg->{plugin} = 'telegram';
$redismsg->{misc}->{answer} = 1;
$redismsg->{misc}->{csign} = "$c->{telegrambot}->{csign}";
$redismsg->{misc}->{msg_format} = 0;
$redismsg->{misc}->{fwd_cnt} = 1;
$redismsg->{misc}->{good_morning} = 0;
sub Command {
my $self = shift;
@@ -82,9 +86,10 @@ sub Command {
# Полноценный поиск простой команды во входящем сообщении
$#cmds = -1;
@cmds = qw (ping пинг пинх pong понг понх coin монетка roll dice кости ver version версия хэлп halp kde кде lat
лат friday пятница proverb пословица fortune фортунка f ф anek анек анекдот buni cat кис drink праздник fox лис
frog лягушка horse лошадь лошадка monkeyuser owl сова сыч rabbit bunny кролик snail улитка tits boobs tities
boobies сиси сисечки butt booty ass попа попка xkcd dig копать fish fishing рыба рыбка рыбалка karma карма);
лат friday пятница proverb пословица пословиться fortune фортунка f ф anek анек анекдот buni cat кис
drink праздник fox лис frog лягушка horse лошадь лошадка monkeyuser owl сова сыч rabbit bunny кролик
snail улитка tits boobs tities boobies сиси сисечки butt booty ass попа попка xkcd dig копать fish
fishing рыба рыбка рыбалка karma карма fuck w п weather погода погодка погадка);
my $bingo = 0;
@@ -103,7 +108,6 @@ sub Command {
# Если команда найдена...
if ($bingo) {
$rmsg->{message} = "$text";
$self->log->debug ('[DEBUG] Sending message to redis ' . Dumper ($rmsg));
# Для некоторых команд мы хотим получать форматированный вывод
$#cmds = -1;
@@ -130,6 +134,15 @@ sub Command {
}
}
# Если тип группы supergroup, в ней могут быть треды, попробуем найти message_thread_id, если таковой есть
if ($msg->chat->type eq 'supergroup') {
if ($msg->can ('is_topic_message') && $msg->is_topic_message) {
if ($msg->can ('message_thread_id') && $msg->message_thread_id ne '') {
$rmsg->{threadid} = "$msg->message_thread_id";
}
}
}
# Если соединения с редиской нету, ждём, пока оно появится. Своего рода костыль на всякий случай.
{
do {
@@ -138,7 +151,7 @@ sub Command {
} while (sleep 1);
}
$log->debug ('Outgoing redis message: ' . Dumper ($rmsg));
$log->debug ('[DEBUG] Outgoing redis message: ' . Dumper ($rmsg));
my $pubsub = $main::REDIS->pubsub;
@@ -173,11 +186,11 @@ ${csign}ping | ${csign}пинг - попинговать бота
${csign}roll | ${csign}dice | ${csign}кости - бросить кости
${csign}snail | ${csign}улитка - улитка
${csign}ver | ${csign}version | ${csign}версия - что-то про версию ПО
${csign}w город | ${csign}п город - погода в указанном городе
${csign}weather город - погода в указанном городе
${csign}погода город - погода в указанном городе
${csign}погодка город - погода в указанном городе
${csign}погадка город - погода в указанном городе
${csign}w <город> | ${csign}п <город> - погода в указанном городе
${csign}weather <город> - погода в указанном городе
${csign}погода <город> - погода в указанном городе
${csign}погодка <город> - погода в указанном городе
${csign}погадка <город> - погода в указанном городе
${csign}xkcd - рандомный стрип с сайта xkcd.ru
${csign}karma фраза | ${csign}карма фраза - посмотреть карму фразы
фраза-- | фраза++ - убавить или добавить карму фразе
@@ -185,14 +198,31 @@ ${csign}karma фраза | ${csign}карма фраза - посмотреть
Но на самом деле я бот больше для общения, чем для исполнения команд.
Поговоришь со мной?
MYHELP
$msg->replyMd ("$reply");
my $res = $msg->replyMd ("$reply");
if ($res->{error}) {
$log->error ('[ERROR] Unable to call sendMessage BotAPI method: ' . Dumper ($res));
}
return;
# Если команда не найдена, но это команда admin
} elsif ($cmd =~ /^(admin|админ)$/u) {
my $member = $self->getChatMember ({ 'chat_id' => 0 + $msg->chat->id, 'user_id' => 0 + $msg->from->id });
# Если команда не найдена, но это команда admin :)
} elsif ($cmd =~ /^(admin|админ)(\s+|\s+.*)?$/u) {
my $send_args->{chat_id} = 0 + $chatid;
$send_args->{user_id} = 0 + $msg->from->id;
my $member = $self->getChatMember ($send_args);
if ($member->{error}) {
$log->error ('[ERROR] Unable to call getChatMember BotAPI method: ' . Dumper ($member));
return;
}
# Это должно показываться только админам чата
if (($member->status eq 'administrator') || ($member->status eq 'creator')) {
if (($member->status ne 'administrator') && ($member->status ne 'creator')) {
$log->debug ("[DEBUG] Non admin member called ${csign}admin command, " . $member->status);
return;
}
if ($cmd =~ /^(admin|админ)\s*$/u) {
$reply = << "MYADMIN";
```
${csign}admin censor type # - где 1 - вкл, 0 - выкл цензуры для означенного типа сообщений
@@ -213,47 +243,78 @@ ${csign}admin obutts # - где 1 - вкл, 0 - выкл плагина o
${csign}admin obutts - показываем ли попки по просьбе участников чата (команды ${csign}ass, ${csign}butt, ${csign}booty, ${csign}попа, ${csign}попка)
${csign}admin chan_msg - оставляем ли сообщения присланные от имени (других) каналов
${csign}admin chan_msg # - где 1 - оставляем, 0 - удаляем
${csign}admin ban userid sec - выдаём ban указанному user-у на указанное количество секунд (от 30 сек до 1 года), доступно только создателю чата
${csign}admin mute userid sec - выдаём mute указанному user-у на указанное количество секунд (от 30 сек до 1 года), доступно только создателю чата
${csign}admin admin mute - разрешено ли обычным админам мьютить участников чата через бота (если бот - админ), (создатель чата всегда может попросить бота-админа замьютить обычного участника чата)
${csign}admin admin mute # - где 1 - разрешено, 0 - не разрешено
```
Типы сообщений:
audio voice photo video animation sticker dice game poll document
MYADMIN
$msg->replyMd ($reply);
}
my $res = $msg->replyMd ($reply);
return;
# Если строка не найдена, но это команда admin с параметрами
} elsif ($cmd =~ /^admin\s+.+$/u || $cmd =~ /^админ\s+.+$/u) {
my $member = $self->getChatMember ({ 'chat_id' => 0 + $msg->chat->id, 'user_id' => 0 + $msg->from->id });
if ($res->{error}) {
$log->error('[ERROR] Unable to call sendMessage BotAPI method: ' . Dumper($res));
}
# Мьютить через бота можно только создателю чятика
if (($member->status eq 'creator') && $cmd =~ /^(admin|админ)\s+mute\s+(\d+)\s+(\d+)$/gu) {
my (undef, undef, $user_id_to_mute, $time) = split /\s+/, $cmd;
return;
} elsif ($cmd =~ /^admin\s+(admin\s+mute)(\s*|\s+0|\s+1)$/gu) {
my $arg;
$arg = trim $2 if (defined $2);
# Бот должен быть админом, чтобы мьютить юзеров
if (defined $arg && $arg !~ /^\s*$/ && ($member->status eq 'creator')) {
if ($arg == 1) {
$reply = MuteByAdminToggle ($chatid, 1);
} elsif ($arg == 0) {
$reply = MuteByAdminToggle ($chatid, 0);
}
} else {
$reply = MuteByAdminStatus ($chatid);
}
} elsif ($cmd =~ /^(admin|админ)\s+(ban|mute)\s+(\d+)\s+(\d+)$/gu) {
if ($member->status eq 'admin' && (! MuteByAdminEnabled ($chatid))) {
return 'Не буду я для тебя никого мьтить.';
}
my (undef, $action, $user_id_to_prosecute, $time) = split /\s+/, $cmd;
# Бот должен быть админом, чтобы банить юзеров
my $me = $self->getMe ();
if (defined $me->{error}) {
if ($me->{error}) {
$log->error ('[ERROR] Unable to call getMe BotAPI method: ' . Dumper ($member));
return;
}
my $myId = $me->id;
if ($myId == $user_id_to_mute) {
$reply = 'Я не буду себя мьютить.';
if ($myId == $user_id_to_prosecute) {
if ($action eq 'mute') {
$reply = 'Я не буду себя мьютить.';
} else {
$reply = 'Я не буду себя банить.';
}
} else {
$me = $self->getChatMember ({'chat_id' => $chatid, 'user_id' => $me->id});
$send_args = undef;
$send_args->{chat_id} = 0 + $chatid;
$send_args->{user_id} = 0 + $me->id;
$me = $self->getChatMember ($send_args);
if (defined $me->{error}) {
if ($me->{error}) {
$log->error ('[ERROR] Unable to call getChatMember BotAPI method: ' . Dumper ($me));
return;
}
if ($me->status eq 'administrator') {
# Проверим, что такой юзер есть в чятике
my $chatMember = $self->getChatMember ({'chat_id' => $chatid, 'user_id' => $user_id_to_mute});
$send_args = undef;
$send_args->{chat_id} = 0 + $chatid;
$send_args->{user_id} = 0 + $user_id_to_prosecute;
my $chatMember = $self->getChatMember ($send_args);
if (defined $chatMember->{error}) {
if ($chatMember->{error}) {
$log->error ('[ERROR] Unable to call getChatMember BotAPI method: ' . Dumper ($chatMember));
return;
}
@@ -261,27 +322,62 @@ MYADMIN
my $memberStatus = $chatMember->status;
if ($memberStatus eq 'creator') {
$reply = 'Ты поехал себя мьютить?';
if ($action eq 'mute') {
$reply = 'Ты поехал себя мьютить?';
} else {
$reply = 'Ты поехал себя банить?';
}
} elsif ($memberStatus eq 'administrator') {
$reply = 'Админов мьютить не буду.';
if ($action eq 'mute') {
$reply = 'Админов мьютить не буду.';
} else {
$reply = 'Админов банить не буду.';
}
} else {
if ($time < 30) {
$reply = 'Ты слишком добр.';
} elsif ($time > (60 * 60 * 24 * 366)) {
$reply = 'Мьют более чем на 366 дней - перманентный, ты слишком жесток, я так не играю.';
} else {
my $result = $self->banChatMember (
{
'chat_id' => 0 + $chatid,
'user_id' => 0 + $user_id_to_mute,
'until_date' => $time + time (),
},
);
if ($result->{error}) {
$reply = 'Что-то пошло не так, не получается замьютить.';
if ($action eq 'mute') {
$reply = 'Мьют более чем на 366 дней - перманентный, ты слишком жесток, я так не играю.';
} else {
$reply = 'Бан более чем на 366 дней - перманентный, ты слишком жесток, я так не играю.';
}
} else {
my $result;
$send_args = undef;
$send_args->{chat_id} = 0 + $chatid;
$send_args->{user_id} = 0 + $user_id_to_prosecute;
$send_args->{until_date} = $time + time ();
if ($action eq 'mute') {
$result = $self->muteChatMember ($send_args);
} else {
$result = $self->banChatMember ($send_args);
}
# Если всё хорошо, то возвращается true в формате JSON::PP::Boolean
if ((ref ($result) eq 'JSON::PP::Boolean') && ($result == JSON::PP::true)) {
$reply = 'Готово.';
} elsif (defined ($result->{error}) && $result->{error}) {
if ($action eq 'mute') {
$log->error (
'[ERROR] Unable to call muteChatMember BotAPI method: ' .
Dumper ($result)
);
$reply = 'Что-то пошло не так, не получается замьютить.';
} else {
$log->error (
'[ERROR] Unable to call banChatMember BotAPI method: ' .
Dumper ($result)
);
$reply = 'Что-то пошло не так, не получается забанить.';
}
} else {
$log->error (
'[ERROR] Unable to call banChatMember/muteChatMember BotAPI method: ' .
Dumper ($result)
);
$reply = 'Что-то пошло совсем не так.';
}
}
}
@@ -289,105 +385,106 @@ MYADMIN
$reply = 'Я так пока не умею, я здесь не админ.'
}
}
} elsif ($cmd =~ /^admin\s+(censor|ценз)\s*$/gu) {
$reply = ListForbidden ($chatid);
} elsif ($cmd =~ /^admin\s+(censor|ценз)\s+(type|тип)\s+(.+)$/gu) {
my $arg;
$arg = trim $3 if (defined $3);
# Это должно показываться только админам чата
} elsif (($member->status eq 'administrator') || ($member->status eq 'creator')) {
# Вынем субкоманду, это первый аргумент команды admin
$cmd =~ /^(admin|админ)\s+(.+)$/u;
my $command = trim ($2); ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
my ($subcmd, $args) = split /\s+/, $command, 2;
if (defined $arg && $arg ne '') {
my ($msgType, $toggle) = split /\s/, $arg, 2;
# Субкоманды не нашлось, ну и какбэ досвидонья
if ($subcmd eq '') {
return;
# Субкоманда censor...
} elsif ($subcmd eq 'censor' || $subcmd eq 'ценз') {
# Censor с аргументами
if (defined ($args) && ($args ne '')) {
my ($msgType, $toggle) = split /\s/, $args, 2;
if (defined $toggle) {
foreach (@ForbiddenMessageTypes) {
if ($msgType eq $_) {
if ($toggle == 1) {
AddForbiddenType ($chatid, $msgType);
$reply = "Теперь сообщения с $msgType будут автоматически удаляться";
} elsif ($toggle == 0) {
DelForbiddenType ($chatid, $msgType);
$reply = "Теперь сообщения с $msgType будут оставаться";
}
if (defined $toggle) {
foreach (@ForbiddenMessageTypes) {
if ($msgType eq $_) {
if ($toggle == 1) {
AddForbiddenType ($chatid, $msgType);
$reply = "Теперь сообщения с $msgType будут автоматически удаляться";
} elsif ($toggle == 0) {
DelForbiddenType ($chatid, $msgType);
$reply = "Теперь сообщения с $msgType будут оставаться";
}
}
}
# Censor без аргументов, выдаёт список типов сообщений и будут ли они автоматически удаляться
} else {
$reply = ListForbidden ($chatid);
}
# Хотим ли мы показывать фортунку с утра
} elsif ($subcmd eq 'fortune' || $subcmd eq 'фортунка') {
if (defined $args) {
if ($args == 1) {
$reply = FortuneToggle ($chatid, 1);
} elsif ($args == 0) {
$reply = FortuneToggle ($chatid, 0);
}
} else {
$reply = FortuneStatus ($chatid);
}
} elsif ($cmd =~ /^admin\s+(fortune|фортунка)(\s*|\s+0|\s+1)$/gu) {
my $arg;
$arg = trim $2 if (defined $2);
if (defined $arg && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = FortuneToggle ($chatid, 1);
} elsif ($arg == 0) {
$reply = FortuneToggle ($chatid, 0);
}
# Хотим ли мы удалять "сообщения от каналов"
} elsif ($subcmd eq 'chan_msg') {
if (defined $args) {
if ($args == 1) {
$reply = ChanMsgToggle ($chatid, 1);
} elsif ($args == 0) {
$reply = ChanMsgToggle ($chatid, 0);
}
} else {
$reply = ChanMsgStatus ($chatid);
} else {
$reply = FortuneStatus ($chatid);
}
} elsif ($cmd =~ /^admin\s+chan_msg(\s*|\s+0|\s+1)$/gu) {
my $arg = $1;
$arg = trim $1 if (defined $1);
if (defined ($arg) && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = ChanMsgToggle ($chatid, 1);
} elsif ($arg == 0) {
$reply = ChanMsgToggle ($chatid, 0);
}
# Приветствуем ли мы новых участников чата
} elsif ($subcmd eq 'greet' || $subcmd eq 'приветствие') {
if (defined $args) {
if ($args == 1) {
$reply = GreetMsgToggle ($chatid, 1);
} elsif ($args == 0) {
$reply = GreetMsgToggle ($chatid, 0);
}
} else {
$reply = GreetMsgStatus ($chatid);
} else {
$reply = ChanMsgStatus ($chatid);
}
} elsif ($cmd =~ /^admin\s+(greet|приветствие)(\s*|\s+0|\s+1)$/gu) {
my $arg = $2;
$arg = trim $2 if (defined $2);
if (defined ($arg) && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = GreetMsgToggle ($chatid, 1);
} elsif ($arg == 0) {
$reply = GreetMsgToggle ($chatid, 0);
}
} elsif ($subcmd eq 'goodbye' || $subcmd eq 'прощание') {
if (defined $args) {
if ($args == 1) {
$reply = GoodbyeMsgToggle ($chatid, 1);
} elsif ($args == 0) {
$reply = GoodbyeMsgToggle ($chatid, 0);
}
} else {
$reply = GoodbyeMsgStatus ($chatid);
} else {
$reply = GreetMsgStatus ($chatid);
}
} elsif ($cmd =~ /^admin\s+(goodbye|прощание)(\s*|\s+0|\s+1)$/gu) {
my $arg = $2;
$arg = trim $2 if (defined $2);
if (defined ($arg) && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = GoodbyeMsgToggle ($chatid, 1);
} elsif ($arg == 0) {
$reply = GoodbyeMsgToggle ($chatid, 0);
}
# Работает ли плагин oboobs в чатике
} elsif ($subcmd eq 'oboobs') {
if (defined $args) {
if ($args == 1) {
$reply = PluginToggle ($chatid, 'oboobs', 1);
} elsif ($args == 0) {
$reply = PluginToggle ($chatid, 'oboobs', 0);
}
} else {
$reply = PluginStatus ($chatid, 'oboobs');
} else {
$reply = GoodbyeMsgStatus ($chatid);
}
} elsif ($cmd =~ /^admin\s+oboobs(\s*|\s+0|\s+1)$/gu) {
my $arg = $1;
$arg = trim $1 if (defined $1);
if (defined ($arg) && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = PluginToggle ($chatid, 'oboobs', 1);
} elsif ($arg == 0) {
$reply = PluginToggle ($chatid, 'oboobs', 0);
}
# Работает ли плагин obutts в чатике
} elsif ($subcmd eq 'obutts') {
if (defined $args) {
if ($args == 1) {
$reply = PluginToggle ($chatid, 'obutts', 1);
} elsif ($args == 0) {
$reply = PluginToggle ($chatid, 'obutts', 0);
}
} else {
$reply = PluginStatus ($chatid, 'obutts');
} else {
$reply = PluginStatus ($chatid, 'oboobs');
}
} elsif ($cmd =~ /^admin\s+obutts(\s*|\s+0|\s+1)$/gu) {
my $arg = $1;
$arg = trim $1 if (defined $1);
if (defined ($arg) && $arg !~ /^\s*$/) {
if ($arg == 1) {
$reply = PluginToggle ($chatid, 'obutts', 1);
} elsif ($arg == 0) {
$reply = PluginToggle ($chatid, 'obutts', 0);
}
} else {
$reply = PluginStatus ($chatid, 'obutts');
}
}
}
@@ -395,85 +492,6 @@ MYADMIN
return $reply;
}
sub Highlight {
my $msg = shift;
my $fullname;
my $highlight;
my $username;
my $visavi = '';
my $userid = $msg->from->id;
if ($msg->from->can ('username') && defined $msg->from->username ) {
$username = $msg->from->username;
}
if ($msg->from->can ('first_name') && defined $msg->from->first_name) {
$fullname = $msg->from->first_name;
if ($msg->from->can ('last_name') && defined $msg->from->last_name) {
$fullname .= ' ' . $msg->from->last_name;
}
} elsif ($msg->from->can ('last_name') && defined $msg->from->last_name) {
$fullname .= $msg->from->last_name;
}
if (defined $username) {
$visavi .= '@' . $username;
if (defined $fullname) {
$highlight = "[$fullname](tg://user?id=$userid)";
$visavi .= ', ' . $fullname;
} else {
$highlight = "[$username](tg://user?id=$userid)";
}
} else {
$highlight = "[$fullname](tg://user?id=$userid)";
$visavi .= $fullname;
}
$visavi .= " ($userid)";
return ($userid, $username, $fullname, $highlight, $visavi);
}
sub BotSleep {
# TODO: Parametrise this with fuzzy sleep time in seconds
my $msg = shift;
# let's emulate real human and delay answer
sleep (irand (2) + 1);
for (0..(4 + irand (3))) {
$msg->typing ();
sleep 3;
sleep 3 unless ($_);
}
sleep ( 3 + irand (2));
return;
}
sub IsCensored {
my $msg = shift;
my $forbidden = GetForbiddenTypes ($msg->chat->id);
# voice messages are special
if (defined ($msg->voice) && defined ($msg->voice->duration) && ($msg->voice->duration > 0)) {
if ($forbidden->{'voice'}) {
return 1;
}
}
foreach (keys %{$forbidden}) {
if ($forbidden->{$_} && (defined $msg->{$_})) {
return 1;
}
}
return 0;
}
1;
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:

View File

@@ -15,10 +15,19 @@ use version; our $VERSION = qw (1.1);
use Exporter qw (import);
# to export array we need @ISA here
our @ISA = qw / Exporter /; ## no critic (ClassHierarchies::ProhibitExplicitISA)
our @EXPORT_OK = qw (@ForbiddenMessageTypes @PluginList GetForbiddenTypes AddForbiddenType DelForbiddenType
ListForbidden FortuneToggle FortuneToggleList FortuneStatus PluginToggle PluginStatus
PluginEnabled ChanMsgToggle ChanMsgStatus ChanMsgEnabled GreetMsgToggle GreetMsgStatus
GreetMsgEnabled GoodbyeMsgToggle GoodbyeMsgStatus GoodbyeMsgEnabled);
our @EXPORT_OK = qw (@ForbiddenMessageTypes @PluginList
GetForbiddenTypes AddForbiddenType DelForbiddenType ListForbidden EnabledForbidden
FortuneToggle FortuneStatus FortuneEnabled FortuneToggleList
ChanMsgToggle ChanMsgStatus ChanMsgEnabled ChanMsgToggleList
GreetMsgToggle GreetMsgStatus GreetMsgEnabled GreetMsgToggleList
GoodbyeMsgToggle GoodbyeMsgStatus GoodbyeMsgEnabled GoodbyeMsgToggleList
PluginToggle PluginStatus PluginEnabled
MuteByAdminToggle MuteByAdminStatus MuteByAdminEnabled MuteByAdminToggleList
MigrateSettingsToNewChatID
IsCensored
GetSlackGreetRememberId
SetSlackGreetRememberId
);
my $c = LoadConf ();
my $cachedir = $c->{cachedir};
@@ -108,6 +117,29 @@ sub ListForbidden {
return join "\n", @list;
}
sub EnabledForbidden {
my $chatid = shift;
my $ftype = shift;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'censor' . '_' . utf2sha1 ($chatid),
);
my $type = $cache->get ($ftype);
if ($type) {
return 1;
} else {
unless (defined $type) {
$cache->set ($ftype, 0, 'never');
}
return 0;
}
}
sub FortuneToggle (@) {
my $chatid = shift;
my $action = shift // undef;
@@ -166,6 +198,24 @@ sub FortuneStatus ($) {
return $phrase;
}
sub FortuneEnabled ($) {
my $chatid = shift;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'fortune',
);
my $state = $cache->get ($chatid);
if (defined $state && $state) {
return 1;
} else {
return 0;
}
}
sub FortuneToggleList () {
my $cache = CHI->new (
driver => 'BerkeleyDB',
@@ -288,33 +338,28 @@ sub GreetMsgToggle (@) {
namespace => __PACKAGE__ . '_' . 'greet_msg',
);
my $state = $cache->get ($chatid);
if (defined $action) {
if ($action) {
$cache->set ($chatid, 1, 'never');
$phrase .= 'включено.';
if ($action == 0) {
$cache->set ($chatid, 0, 'never');
$phrase .= 'выключены.';
} else {
if (defined $state && $state) {
$cache->set ($chatid, 0, 'never');
$phrase .= 'выключено.';
} else {
$cache->set ($chatid, 1, 'never');
$phrase .= 'включено.';
}
$cache->set ($chatid, 1, 'never');
$phrase .= 'включены.';
}
} else {
if (defined $state){
if ($state) {
my $state = $cache->get ($chatid);
if (defined $state) {
if ($state != 0) {
$cache->set ($chatid, 0, 'never');
$phrase .= 'выключено.';
$phrase .= 'выключены.';
} else {
$cache->set ($chatid, 1, 'never');
$phrase .= 'включено.';
$phrase .= 'включены.';
}
} else {
$cache->set ($chatid, 0, 'never');
$phrase .= 'выключено.';
$phrase .= 'выключены.';
}
}
@@ -368,6 +413,16 @@ sub GreetMsgEnabled ($) {
}
}
sub GreetMsgToggleList () {
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'greet_msg',
);
return $cache->get_keys ();
}
sub GoodbyeMsgToggle (@) {
my $chatid = shift;
my $action = shift // undef;
@@ -454,6 +509,16 @@ sub GoodbyeMsgEnabled ($) {
}
}
sub GoodbyeMsgToggleList () {
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'goodbye_msg',
);
return $cache->get_keys ();
}
sub PluginStatus (@) {
my $chatid = shift;
my $plugin = shift;
@@ -539,6 +604,209 @@ sub PluginToggle (@) {
return $phrase;
}
sub MuteByAdminToggle (@) {
my $chatid = shift;
my $action = shift // undef;
my $phrase = 'Мьют участников чата админами ';
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'admin_mute',
);
my $state = $cache->get ($chatid);
if (defined $action) {
if ($action) {
$cache->set ($chatid, 1, 'never');
$phrase .= 'разрешён.';
} else {
$cache->set ($chatid, 0, 'never');
$phrase .= 'запрещён.';
}
} else {
if (defined $state){
if ($state) {
$cache->set ($chatid, 0, 'never');
$phrase .= 'запрещён.';
} else {
$cache->set ($chatid, 1, 'never');
$phrase .= 'разрешён.';
}
} else {
$cache->set ($chatid, 1, 'never');
$phrase .= 'разрешён.';
}
}
return $phrase;
}
sub MuteByAdminStatus ($) {
my $chatid = shift;
my $phrase = 'Мьют админами участников чата ';
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'admin_mute',
);
my $state = $cache->get ($chatid);
if (defined $state) {
if ($state) {
$phrase .= 'разрешён.';
} else {
$phrase .= 'запрещён.';
}
} else {
$phrase .= 'запрещён.';
}
return $phrase;
}
sub MuteByAdminEnabled ($) {
my $chatid = shift;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'admin_mute',
);
my $state = $cache->get ($chatid);
if (defined $state) {
if ($state) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
sub MuteByAdminToggleList () {
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'admin_mute',
);
return $cache->get_keys ();
}
sub MigrateSettingsToNewChatID {
my $old_id = shift;
my $new_id = shift;
# TODO: Catch errors, if error==1, than noop
if (FortuneEnabled ($old_id)) {
FortuneToggle ($new_id, 1);
FortuneToggle ($old_id, 0);
} else {
FortuneToggle ($new_id, 0);
}
if (ChanMsgEnabled ($old_id)) {
ChanMsgToggle ($new_id, 1);
} else {
ChanMsgToggle ($new_id, 0);
}
if (GreetMsgEnabled ($old_id)) {
GreetMsgToggle ($new_id, 1);
} else {
GreetMsgToggle ($new_id, 0);
}
if (GoodbyeMsgEnabled ($old_id)) {
GoodbyeMsgToggle ($new_id, 1);
} else {
GoodbyeMsgToggle ($new_id, 0);
}
foreach my $plugin (@PluginList) {
if (PluginEnabled ($old_id, $plugin)) {
PluginToggle ($new_id, $plugin, 1);
} else {
PluginToggle ($new_id, $plugin, 0);
}
}
foreach my $ftype (@ForbiddenMessageTypes) {
if (EnabledForbidden($old_id, $ftype)) {
AddForbiddenType($new_id, $ftype);
}
# Looks like we don't need to add forbidden type for allowed types... right?
}
return;
}
sub IsCensored {
my $msg = shift;
my $forbidden = GetForbiddenTypes ($msg->chat->id);
# voice messages are special
if (defined ($msg->voice) && defined ($msg->voice->duration) && ($msg->voice->duration > 0)) {
if ($forbidden->{'voice'}) {
return 1;
}
}
foreach (keys %{$forbidden}) {
if ($forbidden->{$_} && (defined $msg->{$_})) {
return 1;
}
}
return 0;
}
sub GetSlackGreetRememberId ($) {
my $userid = shift;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'slack_greet_remember_id',
);
my $state = $cache->get ($userid);
if (defined $state) {
if ($state) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
sub SetSlackGreetRememberId ($) {
my $userid = shift;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__ . '_' . 'slack_greet_remember_id',
);
$cache->set ($userid, 1, 'never');
return;
}
1;
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:

View File

@@ -14,7 +14,7 @@ use Exporter qw (import);
our @EXPORT_OK = qw (LoadConf);
sub LoadConf {
my $c = 'data/config.json';
my $c = shift // 'data/config.json';
my $CH;
unless (open $CH, '<', $c) {

View File

@@ -8,13 +8,14 @@ use open qw (:std :utf8);
use English qw ( -no_match_vars );
use Digest::SHA qw (sha1_base64);
use Encode qw (encode_utf8);
use Math::Random::Secure qw (irand);
use MIME::Base64 qw (encode_base64);
use Text::Fuzzy qw (distance_edits);
use URI::URL qw (url);
use version; our $VERSION = qw (1.0);
use Exporter qw (import);
our @EXPORT_OK = qw (trim urlencode fmatch utf2b64 utf2sha1);
our @EXPORT_OK = qw (trim urlencode fmatch utf2b64 utf2sha1 BotSleep Highlight RandomCommonPhrase);
sub trim {
my $str = shift;
@@ -72,6 +73,110 @@ sub utf2sha1 {
return sha1_base64 $bytes;
}
sub Highlight {
my $msg = shift;
my $fullname;
my $highlight;
my $username;
my $visavi = '';
my $userid;
# In channel message $msg->from->id is omitted
# In channel context Highlight is pointless
if ($msg->chat->type eq 'channel') {
return ($userid, $username, $fullname, $highlight, undef);
}
if ($msg->can ('from')) {
$userid = $msg->from->id;
if ($msg->from->can ('username') && defined $msg->from->username ) {
$username = $msg->from->username;
}
if ($msg->from->can ('first_name') && defined $msg->from->first_name) {
$fullname = $msg->from->first_name;
if ($msg->from->can ('last_name') && defined $msg->from->last_name) {
$fullname .= ' ' . $msg->from->last_name;
}
} elsif ($msg->from->can ('last_name') && defined $msg->from->last_name) {
$fullname .= $msg->from->last_name;
}
} else {
# Reacions case.
$userid = $msg->user->id;
if ($msg->user->can ('username') && defined $msg->user->username ) {
$username = $msg->user->username;
}
if ($msg->user->can ('first_name') && defined $msg->user->first_name) {
$fullname = $msg->user->first_name;
if ($msg->user->can ('last_name') && defined $msg->user->last_name) {
$fullname .= ' ' . $msg->user->last_name;
}
} elsif ($msg->user->can ('last_name') && defined $msg->user->last_name) {
$fullname .= $msg->user->last_name;
}
}
if (defined $username) {
$visavi .= '@' . $username;
if (defined $fullname) {
$highlight = "[$fullname](tg://user?id=$userid)";
$visavi .= ', ' . $fullname;
} else {
$highlight = "[$username](tg://user?id=$userid)";
}
} else {
$highlight = "[$fullname](tg://user?id=$userid)";
$visavi .= $fullname;
}
$visavi .= " ($userid)";
return ($userid, $username, $fullname, $highlight, $visavi);
}
sub RandomCommonPhrase {
my @myphrase = (
'Так, блядь...',
'*Закатывает рукава* И ради этого ты меня позвал?',
'Ну чего ты начинаешь, нормально же общались',
'Повтори свой вопрос, не поняла',
'Выйди и зайди нормально',
'Я подумаю',
'Даже не знаю, что на это ответить',
'Ты упал такие вопросы девочке задавать?',
'Можно и так, но не уверена',
'А как ты думаешь?',
'А ви, таки, почему интересуетесь?',
);
return $myphrase[irand ($#myphrase + 1)];
}
sub BotSleep {
# TODO: Parametrise this with fuzzy sleep time in seconds
my $msg = shift;
# let's emulate real human and delay answer
sleep (irand (2) + 1);
for (0..(4 + irand (3))) {
$msg->typing ();
sleep 3;
sleep 3 unless ($_);
}
sleep ( 3 + irand (2));
return;
}
1;
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:

View File

@@ -4,15 +4,17 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use strict;
use warnings;
use utf8;
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use open qw (:std :utf8);
use Data::Dumper qw (Dumper);
use Log::Any qw ($log);
use Mojo::Redis ();
use Data::Dumper qw (Dumper);
use JSON::XS qw (decode_json);
use Log::Any qw ($log);
use Mojo::Redis ();
use Mojo::Redis::Connection ();
use BotLib::Conf qw (LoadConf);
use Teapot::Bot::Object::ChatPermissions ();
use BotLib::Admin qw (MigrateSettingsToNewChatID FortuneToggle);
use BotLib::Conf qw (LoadConf);
use version; our $VERSION = qw (1.0);
use Exporter qw (import);
@@ -41,25 +43,76 @@ sub redis_parse_message {
$message->{chat_id} = eval { 0 + $m->{chatid}; };
unless (defined $message->{chat_id}) {
$log->error ('Incoming redis message is incorrect: chat id must be numeric value!');
$log->error ('[ERROR] Incoming redis message is incorrect: chat id must be numeric value!');
return;
}
if ($message->{chat_id} == 0) {
$log->error ('Incoming redis message is incorrect: chat id must not be equal 0!');
$log->error ('[ERROR] Incoming redis message is incorrect: chat id must not be equal 0!');
return;
}
$message->{text} = "$m->{message}";
my $can_talk = Teapot::Bot::Object::ChatPermissions->canTalk ($main::TGM, 0 + $m->{chatid});
if (defined ($m->{threadid}) && $m->{threadid} ne '') {
$message->{message_thread_id} = 0 + $m->{threadid};
}
if ($can_talk) {
# Результат этого действия нас не сильно волнует, т.к. если будет ошибка, то в лог попадёт трейс
# we cannot use $self->_brain->can_talk here because of $self pointing to redis object, not telegram
my $can_talk = $main::TGM->can_talk ({ chat_id => 0 + $m->{chatid}});
if ($can_talk->{error}) {
$log->error ('[ERROR] Unable to guess if i can talk in this chat: ' . Dumper ($can_talk));
return;
}
if ($can_talk->{can_talk}) {
my $r = $main::TGM->sendMessage ($message);
if ($r->{error}) {
$log->error ("An error occured while sending message to chat $m->{chatid}");
# TODO: handle 50x errors? They occur when api servers being updated.
my $resp = eval { $r->{debug}->json };
if (defined $resp) {
if (defined ($resp->{error_code}) && $resp->{error_code} == 400) {
if (defined $resp->{description}) {
if ($resp->{description} eq 'Bad Request: not enough rights to send text messages to the chat') {
$log->notice ("[NOTICE] Have no rights to send message to $m->{chatid}");
# TODO: disable fortune if message is good morning fortune
} elsif ($resp->{description} eq 'Bad Request: chat not found') {
$log->notice (
"[NOTICE] Chat not found toggling good morning fortune off for $m->{chatid}"
);
FortuneToggle($m->{chatid}, 0);
return;
} elsif ($resp->description eq 'Bad Request: group chat was upgraded to a supergroup chat') {
if (defined ($resp->{parameters}) &&
defined ($resp->{parameters}->{migrate_to_chat_id})) {
my $tpl = '[NOTICE] group upgraded to supergroup, chat_id changed, ';
$tpl .= 'migrating settings from %s to %s';
$log->notice (
sprintf($tpl, $m->{chatid}, $resp->{parameters}->{migrate_to_chat_id})
);
MigrateSettingsToNewChatID (
$m->{chatid},
$resp->{parameters}->{migrate_to_chat_id}
);
}
} else {
$log->error ('[ERROR] Unable to call sendMessage() BotAPI method: ' . Dumper ($r));
}
} else {
$log->error ('[ERROR] BotAPI returns parsable error: ' . Dumper ($resp));
}
} else {
$log->error ('[ERROR] Unable to handle BotAPI error: ' . Dumper ($resp));
}
} else {
$log->error ('[ERROR] Unable to call sendMessage() BotAPI method: ' . Dumper ($r));
}
}
}

View File

@@ -4,7 +4,7 @@ use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
$Teapot::Bot::VERSION = '0.023';
$Teapot::Bot::VERSION = '0.026';
# ABSTRACT: A base class to make your very own Telegram bot
1;
@@ -25,7 +25,7 @@ A base class to make your very own Telegram bot
=head1 VERSION
version 0.023
version 0.026
=head1 SYNOPSIS

File diff suppressed because it is too large Load Diff

View File

@@ -9,14 +9,14 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PhotoSize ();
$Teapot::Bot::Object::Animation::VERSION = '0.023';
$Teapot::Bot::Object::Animation::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
has 'width';
has 'height';
has 'duration';
has 'thumb'; #PhotoSize
has 'thumbnail'; #PhotoSize
has 'file_name';
has 'mime_type';
has 'file_size';
@@ -24,7 +24,7 @@ has 'file_size';
sub fields {
return { scalar => [qw/file_id file_unique_id width height duration file_name mime_type
file_size/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumb/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumbnail/],
};
}
@@ -43,7 +43,7 @@ Teapot::Bot::Object::Animation - The base class for Telegram message 'Animation'
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Animation' type.

View File

@@ -7,8 +7,8 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Audio::VERSION = '0.023';
use Teapot::Bot::Object::PhotoSize ();
$Teapot::Bot::Object::Audio::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
@@ -18,12 +18,12 @@ has 'title'; # Optional. Title of the audio as defined by sender or by audio
has 'file_name'; # Optional. Original filename as defined by sender
has 'mime_type'; # Optional. MIME type of the file as defined by sender
has 'file_size'; # Optional. File size
has 'thumb'; # Optional. Thumbnail of the album cover to which the music file belongs
has 'thumbnail'; # Optional. Thumbnail of the album cover to which the music file belongs
sub fields {
return { scalar => [qw/file_id file_unique_id duration performer title file_name mime_type
file_size/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumb/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumbnail/],
};
}
@@ -41,7 +41,7 @@ Teapot::Bot::Object::Audio - The base class for Telegram 'Audio' type objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Audio' type objects.

View File

@@ -13,7 +13,7 @@ use List::Util qw/any/;
use Mojo::Base -base;
$Teapot::Bot::Object::Base::VERSION = '0.023';
$Teapot::Bot::Object::Base::VERSION = '0.026';
has '_brain'; # a reference to our brain
@@ -187,7 +187,7 @@ Teapot::Bot::Object::Base - The base class for all Telegram::Bot::Object objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION

View File

@@ -7,7 +7,7 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::CallbackGame::VERSION = '0.023';
$Teapot::Bot::Object::CallbackGame::VERSION = '0.026';
# https://core.telegram.org/bots/api#callbackgame
# "A placeholder, currently holds no information. Use BotFather to set up your game"
@@ -30,7 +30,7 @@ Teapot::Bot::Object::CallbackGame - The base class for Telegram message 'Callbac
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'CallbackGame' type.

View File

@@ -12,7 +12,7 @@ use Teapot::Bot::Object::Message ();
use Teapot::Bot::Object::ChatPermissions ();
use Teapot::Bot::Object::ChatLocation ();
$Teapot::Bot::Object::Chat::VERSION = '0.023';
$Teapot::Bot::Object::Chat::VERSION = '0.026';
has 'id';
has 'type';
@@ -20,11 +20,23 @@ has 'title'; # Optional.
has 'username'; # Optional.
has 'first_name'; # Optional.
has 'last_name'; # Optional.
has 'is_forum'; # Optional. True, if the supergroup chat is a forum (has topics enabled)
has 'photo'; # Teapot::Bot::Object::ChatPhoto. Optional. Returned only in getChat.
has 'active_usernames'; # Optional. If non-empty, the list of all active chat usernames; for private chats,
# supergroups and channels. Returned only in getChat.
has 'emoji_status_custom_emoji_id'; # Optional. Custom emoji identifier of emoji status of the other party in a private
# chat. Returned only in getChat.
has 'bio'; # Optional. Bio of the other party in a private chat. Returned only in getChat.
has 'has_private_forwards'; # Optional. True, if privacy settings of the other party in the private chat allows to
# use tg://user?id=<user_id> links only in chats with the user.
# Returned only in getChat.
has 'has_restricted_voice_and_video_messages'; # Optional. True, if the privacy settings of the other party restrict
# sending voice and video note messages in the private chat.
# Returned only in getChat.
has 'join_to_send_messages'; # Optional. True, if users need to join the supergroup before they can send messages.
# Returned only in getChat.
has 'join_by_request'; # Optional. True, if all users directly joining the supergroup need to be approved by
# supergroup administrators. Returned only in getChat.
has 'description'; # Optional. Returned only in getChat.
has 'invite_link'; # Optional. Returned only in getChat.
has 'pinned_message'; # Teapot::Bot::Object::Message
@@ -33,6 +45,10 @@ has 'permissions'; # Teapot::Bot::Object::ChatPermissions. Optional
has 'slow_mode_delay'; # Optional. Returned only in getChat.
has 'message_auto_delete_time'; # Optional. The time after which all messages sent to the chat will be automatically
# deleted; in seconds. Returned only in getChat.
has 'has_aggressive_anti_spam_enabled'; # Optional. True, if aggressive anti-spam checks are enabled in the supergroup.
# The field is only available to chat administrators. Returned only in getChat.
has 'has_hidden_members'; # Optional. True, if non-administrators can only get the list of bots and administrators
# in the chat. Returned only in getChat.
has 'has_protected_content'; # Optional. True, if messages from the chat can't be forwarded to other chats.
# Returned only in getChat.
has 'sticker_set_name'; # Optional. Returned only in getChat.
@@ -42,10 +58,14 @@ has 'location'; # Optional. Returned only in getChat.
sub fields {
return {
'scalar' => [qw/id type title username first_name last_name bio
has_private_forwards description invite_link slow_mode_delay
message_auto_delete_time has_protected_content
sticker_set_name can_set_sticker_set linked_chat_id/],
'scalar' => [qw/id type title username first_name last_name is_forum
emoji_status_custom_emoji_id bio
has_private_forwards has_restricted_voice_and_video_messages
join_to_send_messages join_by_request description invite_link
slow_mode_delay message_auto_delete_time
has_aggressive_anti_spam_enabled has_hidden_members
has_protected_content sticker_set_name can_set_sticker_set
linked_chat_id/],
'Teapot::Bot::Object::ChatPhoto' => [qw/photo/],
'Teapot::Bot::Object::Message' => [qw/pinned_message/],
'Teapot::Bot::Object::ChatPermissions' => [qw/permissions/],
@@ -53,6 +73,9 @@ sub fields {
};
}
sub arrays {
return qw/active_usernames/;
}
sub is_user {
return shift->id > 0;
@@ -77,7 +100,7 @@ Teapot::Bot::Object::Chat - The base class for Telegram 'Chat' type objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Chat' type objects.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::User ();
$Teapot::Bot::Object::ChatInviteLink::VERSION = '0.023';
$Teapot::Bot::Object::ChatInviteLink::VERSION = '0.026';
has 'invite_link';
has 'creator';
@@ -44,7 +44,7 @@ Teapot::Bot::Object::ChatInviteLink - The base class for Telegram message 'ChatI
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ChatInviteLink' type.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ChatLocation::VERSION = '0.023';
$Teapot::Bot::Object::ChatLocation::VERSION = '0.026';
has 'location';
has 'address';
@@ -34,7 +34,7 @@ Teapot::Bot::Object::ChatLocation - The base class for Telegram message 'ChatLoc
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ChatLocation' type.

View File

@@ -12,7 +12,7 @@ use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::User ();
$Teapot::Bot::Object::ChatMember::VERSION = '0.023';
$Teapot::Bot::Object::ChatMember::VERSION = '0.026';
# ChatMemberOwner
has 'user';
@@ -39,7 +39,8 @@ has 'can_promote_members'; # optional. Administrators only.
has 'can_change_info'; # optional. Administrators and restricted only.
has 'can_invite_users'; # optional. Administrators and restricted only.
has 'can_pin_messages'; # optional. Administrators and restricted only.
has 'can_manage_topics'; # Optional. True, if the user is allowed to create, rename, close, and reopen forum
# topics; supergroups only
# ChatMemberMember
# has 'status';
# has 'user';
@@ -52,11 +53,17 @@ has 'is_member'; # optional. Restricted only.
# has 'can_invite_users'; # optional. Administrators and restricted only.
# has 'can_pin_messages'; # optional. Administrators and restricted only.
has 'can_send_messages'; # optional. Restricted only.
has 'can_send_media_messages'; # optional. Restricted only.
has 'can_send_audios';
has 'can_send_documents';
has 'can_send_photos';
has 'can_send_videos';
has 'can_send_video_notes';
has 'can_send_voice_notes';
has 'can_send_polls'; # optional. Restricted only.
has 'can_send_other_messages'; # optional. Restricted only.
has 'can_add_web_page_previews'; # optional. Restricted only.
has 'until_date'; # optional. Restricted and kicked only.
# has 'can_manage_topics';
# ChatMemberLeft
# has 'status';
@@ -69,11 +76,14 @@ has 'until_date'; # optional. Restricted and kicked only.
sub fields {
return {
'scalar' => [qw/status custom_title is_anonymous can_be_edited can_manage_chat can_post_messages
can_edit_messages can_delete_messages can_manage_voice_chats can_restrict_members
can_promote_members can_change_info can_invite_users can_pin_messages is_member
can_send_messages can_send_media_messages can_send_polls can_send_other_messages
can_add_web_page_previews until_date/],
'scalar' => [qw/status custom_title is_anonymous can_be_edited can_manage_chat
can_post_messages can_edit_messages can_delete_messages
can_manage_voice_chats can_restrict_members can_promote_members
can_change_info can_invite_users can_pin_messages is_member
can_send_messages can_send_audios can_send_documents can_send_photos
can_send_videos can_send_video_notes can_send_voice_notes can_send_polls
can_send_other_messagescan_add_web_page_previews until_date
can_manage_topics/],
'Teapot::Bot::Object::User' => [qw/user/],
};
}
@@ -145,7 +155,7 @@ Teapot::Bot::Object::ChatMember - The base class for Telegram 'ChatMember' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'ChatMember' type objects.

View File

@@ -13,7 +13,7 @@ use Teapot::Bot::Object::ChatMember ();
use Teapot::Bot::Object::ChatInviteLink ();
use Teapot::Bot::Object::Voice ();
$Teapot::Bot::Object::ChatMemberUpdated::VERSION = '0.023';
$Teapot::Bot::Object::ChatMemberUpdated::VERSION = '0.026';
has 'chat';
has 'from';
@@ -50,7 +50,7 @@ Teapot::Bot::Object::ChatMemberUpdated - The base class for Telegram 'ChatMember
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'ChatMemberUpdated' type objects.

View File

@@ -10,13 +10,17 @@ use Carp qw(carp);
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Brain ();
$Teapot::Bot::Object::ChatPermissions::VERSION = '0.023';
$Teapot::Bot::Object::ChatPermissions::VERSION = '0.026';
# all fields are optional
# N.B. Some of the fields "overlaps" other, so in order to guess valid set of permissions you should check all of them
has 'can_send_messages'; # True, if the user is allowed to send text messages, contacts, locations and venues
has 'can_send_media_messages'; # True, if the user is allowed to send audios, documents, photos, videos, video notes
# and voice notes, implies can_send_messages
has 'can_send_audios'; # True, if the user is allowed to send audios
has 'can_send_documents'; # True, if the user is allowed to send documents
has 'can_send_photos'; # True, if the user is allowed to send photos
has 'can_send_videos'; # True, if the user is allowed to send videos
has 'can_send_video_notes'; # True, if the user is allowed to send video_notes
has 'can_send_voice_notes'; # True, if the user is allowed to send voice_notes
has 'can_send_polls'; # True, if the user is allowed to send polls, implies can_send_messages
has 'can_send_other_messages'; # True, if the user is allowed to send animations, games, stickers and use inline bots,
# implies can_send_media_messages
@@ -26,21 +30,52 @@ has 'can_change_info'; # True, if the user is allowed to change the ch
# Ignored in public supergroups
has 'can_invite_users'; # True, if the user is allowed to invite new users to the chat
has 'can_pin_messages'; # True, if the user is allowed to pin messages. Ignored in public supergroups
has 'can_manage_topics'; # Optional. True, if the user is allowed to create forum topics. If omitted defaults to
# the value of can_pin_messages
sub fields {
return {
'scalar' => [qw/can_send_messages can_send_media_messages can_send_polls
can_send_other_messages can_add_web_page_previews can_change_info
can_invite_users can_pin_messages/],
'scalar' => [qw/can_send_messages can_send_audios can_send_documents
can_send_photos can_send_videos can_send_video_notes
can_send_voice_notes can_send_polls can_send_other_messages
can_add_web_page_previews can_change_info can_invite_users
can_pin_messages can_manage_topics/],
};
}
sub canTalk {
my $self = shift;
my $self;
my $args = {};
# There is some magic happen if we call function as object, as we usually do here
# But if we access function from usual-style wrapper that calls it directly from namespace it supply noy $self object
# but string with its name, which is a bummer.
while (my $o = shift @_) {
if (ref ($o) ne '') {
$self = $o;
last;
}
}
my $chatid = shift;
my $args->{chat_id} = $chatid;
$args->{chat_id} = $chatid;
my $can_talk = 0;
my $ret;
if (! defined($self->token) || $self->token eq '') {
$ret->{error} = 1;
$ret->{message} = 'No token supplied to canTalk()';
return $ret;
}
unless ($args->{chat_id}) {
$ret->{error} = 1;
$ret->{message} = 'No chat_id supplied to canTalk()';
return $ret;
}
if ($chatid < 0) {
# group chat
@@ -48,16 +83,23 @@ sub canTalk {
# on api error, keep silence
if ($chatobj == 0 || $chatobj->{error}) {
carp "Unable to get chat info for $chatid from telegram API";
return $can_talk;
my $emesg = "Unable to get chat info for $chatid from telegram API";
$ret->{message} = $emesg;
$ret->{error} = 1;
$ret->{can_talk} = 0;
return $ret;
}
my $myObj = Teapot::Bot::Brain->getMe ($self);
# on api error, keep silence
if ($myObj == 0 || $myObj->{error}) {
carp "Unable to get chat info for $chatid from telegram API";
return $can_talk;
my $emesg = "Unable to get chat info for $chatid from telegram API";
$ret->{message} = $emesg;
$ret->{error} = 1;
$ret->{can_talk} = 0;
return $ret;
}
my $myid = $myObj->id;
@@ -66,8 +108,12 @@ sub canTalk {
# on api error, keep silence
if ($me == 0 || $me->{error}) {
carp 'Unable to get chat info for bot itself from telegram API';
return $can_talk;
my $emesg = 'Unable to get chat info for bot itself from telegram API';
$ret->{message} = $emesg;
$ret->{error} = 1;
$ret->{can_talk} = 0;
return $ret;
}
if ($me->{'status'} eq 'administrator') {
@@ -81,9 +127,13 @@ sub canTalk {
$can_talk = 1;
}
return $can_talk;
$ret->{error} = 0;
$ret->{can_talk} = $can_talk;
return $ret;
}
1;
__END__
@@ -98,7 +148,7 @@ Teapot::Bot::Object::ChatPermissions - The base class for Telegram 'ChatPermissi
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'ChatPermissions' type objects.
@@ -112,7 +162,9 @@ attributes available for C<Teapot::Bot::Object::ChatPermissions> objects.
A convenience method to check if bot can talk in given conversation.
Returns true if the bot can talk in given conversation, otherwise false.
Returns hash reference with can_talk true if the bot can talk in given conversation, otherwise false.
Also set field error 1 if an error occurs during api interaction.
And if error set to true message field also set plus can_talk set to false.
=head1 AUTHOR

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ChatPhoto::VERSION = '0.023';
$Teapot::Bot::Object::ChatPhoto::VERSION = '0.026';
has 'small_file_id';
has 'small_file_unique_id';
@@ -35,7 +35,7 @@ Teapot::Bot::Object::ChatPhoto - The base class for Telegram 'ChatPhoto' type ob
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'ChatPhoto' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Contact::VERSION = '0.023';
$Teapot::Bot::Object::Contact::VERSION = '0.026';
has 'phone_number';
has 'first_name';
@@ -36,7 +36,7 @@ Teapot::Bot::Object::Contact - The base class for Telegram 'Contact' objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Contact' objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Dice::VERSION = '0.023';
$Teapot::Bot::Object::Dice::VERSION = '0.026';
has 'emoji';
has 'value';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::Dice - The base class for Telegram message 'Dice' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Dice' type.

View File

@@ -9,11 +9,11 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PhotoSize ();
$Teapot::Bot::Object::Document::VERSION = '0.023';
$Teapot::Bot::Object::Document::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
has 'thumb'; #PhotoSize
has 'thumbnail'; #PhotoSize
has 'file_name';
has 'mime_type';
has 'file_size';
@@ -21,7 +21,7 @@ has 'file_size';
sub fields {
return {
scalar => [qw/file_id file_unique_id file_name mime_type file_size/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumb/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumbnail/],
};
}
@@ -39,7 +39,7 @@ Teapot::Bot::Object::Document - The base class for Telegram 'Document' objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Document' objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::EncryptedCredentials::VERSION = '0.023';
$Teapot::Bot::Object::EncryptedCredentials::VERSION = '0.026';
has 'data';
has 'hash';
@@ -32,7 +32,7 @@ Teapot::Bot::Object::EncryptedCredentials - The base class for Telegram 'Encrypt
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'EncryptedCredentials' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::EncryptedPassportElement::VERSION = '0.023';
$Teapot::Bot::Object::EncryptedPassportElement::VERSION = '0.026';
# XXX Implement rest of this
# https://core.telegram.org/bots/api#encryptedpassportelement
@@ -38,7 +38,7 @@ Teapot::Bot::Object::EncryptedPassportElement - The base class for Telegram 'Enc
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'EncryptedPassportElement' type objects.

View File

@@ -0,0 +1,51 @@
package Teapot::Bot::Object::ForumTopicClosed;
# ABSTRACT: The base class for Telegram message 'ForumTopicClosed' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ForumTopicClosed::VERSION = '0.026';
sub fields {
return { };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::ForumTopicClosed - The base class for Telegram message 'ForumTopicClosed' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ForumTopicClosed' type.
See L<https://core.telegram.org/bots/api#forumtopicclosed> for details of the
attributes available for C<Teapot::Bot::Object::ForumTopicClosed> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2022 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,54 @@
package Teapot::Bot::Object::ForumTopicCreated;
# ABSTRACT: The base class for Telegram message 'ForumTopicCreated' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ForumTopicCreated::VERSION = '0.026';
has 'name';
has 'icon_color';
has 'icon_custom_emoji_id';
sub fields {
return { scalar => [qw/name icon_color icon_custom_emoji_id/] };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::ForumTopicCreated - The base class for Telegram message 'ForumTopicCreated' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ForumTopicCreated' type.
See L<https://core.telegram.org/bots/api#forumtopiccreated> for details of the
attributes available for C<Teapot::Bot::Object::ForumTopicCreated> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2022 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,55 @@
package Teapot::Bot::Object::ForumTopicEdited;
# ABSTRACT: The base class for Telegram message 'ForumTopicEdited' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ForumTopicEdited::VERSION = '0.026';
has 'name';
has 'icon_custom_emoji_id';
sub fields {
return {
'scalar' => [qw/name icon_custom_emoji_id/],
};
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::ForumTopicEdited - The base class for Telegram message 'ForumTopicEdited' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ForumTopicEdited' type.
See L<https://core.telegram.org/bots/api#forumtopicedited> for details of the
attributes available for C<Teapot::Bot::Object::ForumTopicEdited> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2023 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,51 @@
package Teapot::Bot::Object::ForumTopicReopened;
# ABSTRACT: The base class for Telegram message 'ForumTopicReopened' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ForumTopicReopened::VERSION = '0.026';
sub fields {
return { };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::ForumTopicReopened - The base class for Telegram message 'ForumTopicReopened' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ForumTopicReopened' type.
See L<https://core.telegram.org/bots/api#forumtopicreopened> for details of the
attributes available for C<Teapot::Bot::Object::ForumTopicReopened> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2022 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -11,7 +11,7 @@ use Teapot::Bot::Object::PhotoSize ();
use Teapot::Bot::Object::Animation ();
use Teapot::Bot::Object::MessageEntity ();
$Teapot::Bot::Object::Game::VERSION = '0.023';
$Teapot::Bot::Object::Game::VERSION = '0.026';
has 'title';
has 'description';
@@ -45,7 +45,7 @@ Teapot::Bot::Object::Game - The base class for Telegram message 'Game' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Game' type.

View File

@@ -0,0 +1,51 @@
package Teapot::Bot::Object::GeneralForumTopicHidden;
# ABSTRACT: The base class for Telegram message 'GeneralForumTopicHidden' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::GeneralForumTopicHidden::VERSION = '0.026';
sub fields {
return { };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::GeneralForumTopicHidden - The base class for Telegram message 'GeneralForumTopicHidden' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'GeneralForumTopicHidden' type.
See L<https://core.telegram.org/bots/api#generalforumtopichidden> for details of the
attributes available for C<Teapot::Bot::Object::GeneralForumTopicHidden> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2023 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,51 @@
package Teapot::Bot::Object::GeneralForumTopicUnhidden;
# ABSTRACT: The base class for Telegram message 'GeneralForumTopicUnhidden' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::GeneralForumTopicUnhidden::VERSION = '0.026';
sub fields {
return { };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::GeneralForumTopicUnhidden - The base class for Telegram message 'GeneralForumTopicUnhidden' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'GeneralForumTopicUnhidden' type.
See L<https://core.telegram.org/bots/api#generalforumtopicunhidden> for details of the
attributes available for C<Teapot::Bot::Object::GeneralForumTopicUnhidden> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2023 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -10,7 +10,7 @@ use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::LoginUrl ();
use Teapot::Bot::Object::CallbackGame ();
$Teapot::Bot::Object::InlineKeyboardButton::VERSION = '0.023';
$Teapot::Bot::Object::InlineKeyboardButton::VERSION = '0.026';
has 'text';
has 'url';
@@ -43,7 +43,7 @@ Teapot::Bot::Object::InlineKeyboardButton - The base class for Telegram 'InlineK
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'InlineKeyboardButton' type objects.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::InlineKeyboardButton ();
$Teapot::Bot::Object::InlineKeyboardMarkup::VERSION = '0.023';
$Teapot::Bot::Object::InlineKeyboardMarkup::VERSION = '0.026';
has 'inline_keyboard';
@@ -35,7 +35,7 @@ Teapot::Bot::Object::InlineKeyboardMarkup - The base class for Telegram 'InlineK
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'InlineKeyboardMarkup' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Invoice::VERSION = '0.023';
$Teapot::Bot::Object::Invoice::VERSION = '0.026';
has 'title';
has 'description';
@@ -34,7 +34,7 @@ Teapot::Bot::Object::Invoice - The base class for Telegram 'Invoice' type object
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Invoice' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Location::VERSION = '0.023';
$Teapot::Bot::Object::Location::VERSION = '0.026';
has 'longitude';
has 'latitude';
@@ -35,7 +35,7 @@ Teapot::Bot::Object::Location - The base class for Telegram message 'Location' t
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Location' type.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::LoginUrl::VERSION = '0.023';
$Teapot::Bot::Object::LoginUrl::VERSION = '0.026';
has 'url';
has 'forward_text';
@@ -33,7 +33,7 @@ Teapot::Bot::Object::LoginUrl - The base class for Telegram 'LoginUrl' type obje
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'LoginUrl' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::MaskPosition::VERSION = '0.023';
$Teapot::Bot::Object::MaskPosition::VERSION = '0.026';
has 'point';
has 'x_shift';
@@ -33,7 +33,7 @@ Teapot::Bot::Object::MaskPosition - The base class for Telegram 'MaskPosition' t
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'MaskPosition' type objects.

View File

@@ -30,7 +30,14 @@ use Teapot::Bot::Object::PhotoSize ();
use Teapot::Bot::Object::Invoice ();
use Teapot::Bot::Object::Venue ();
use Teapot::Bot::Object::SuccessfulPayment ();
use Teapot::Bot::Object::WriteAccessAllowed ();
use Teapot::Bot::Object::PassportData ();
use Teapot::Bot::Object::ForumTopicCreated ();
use Teapot::Bot::Object::ForumTopicEdited ();
use Teapot::Bot::Object::ForumTopicClosed ();
use Teapot::Bot::Object::ForumTopicReopened ();
use Teapot::Bot::Object::GeneralForumTopicHidden ();
use Teapot::Bot::Object::GeneralForumTopicUnhidden ();
use Teapot::Bot::Object::InlineKeyboardMarkup ();
use Teapot::Bot::Object::ProximityAlertTriggered ();
use Teapot::Bot::Object::Dice ();
@@ -40,10 +47,12 @@ use Teapot::Bot::Object::VideoChatStarted ();
use Teapot::Bot::Object::VideoChatEnded ();
use Teapot::Bot::Object::VideoChatParticipantsInvited ();
$Teapot::Bot::Object::Message::VERSION = '0.023';
$Teapot::Bot::Object::Message::VERSION = '0.026';
# basic message stuff
has 'message_id';
has 'message_thread_id'; # Optional. Unique identifier of a message thread to which the message belongs;
# for supergroups only
has 'from'; # User
has 'sender_chat'; # Chat
has 'date';
@@ -54,6 +63,7 @@ has 'forward_from_message_id';
has 'forward_signature';
has 'forward_sender_name';
has 'forward_date';
has 'is_topic_message'; # Optional. True, if the message is sent to a forum topic
has 'is_automatic_forward';
has 'reply_to_message'; # Message
has 'via_bot'; # User
@@ -73,6 +83,7 @@ has 'video_note'; # VideoNote
has 'voice'; # Voice
has 'caption';
has 'caption_entities'; # Array of MessageEntity
has 'has_media_spoiler';
has 'contact'; # Contact
has 'dice';
has 'game';
@@ -94,8 +105,15 @@ has 'pinned_message'; # Message
has 'invoice'; # Invoice
has 'successful_payment'; # SuccessfulPayment
has 'connected_website';
has 'write_access_allowed'; # WriteAccessAllowed
has 'passport_data'; # PassportData
has 'proximity_alert_triggered';
has 'forum_topic_created'; # ForumTopicCreated
has 'forum_topic_edited'; # ForumTopicEdited
has 'forum_topic_closed'; # ForumTopicClosed
has 'forum_topic_reopened'; # ForumTopicReopened
has 'general_forum_topic_hidden'; # GeneralForumTopicHidden
has 'general_forum_topic_unhidden'; # GeneralForumTopicUnhidden
has 'video_chat_scheduled';
has 'video_chat_started';
has 'video_chat_ended';
@@ -105,48 +123,55 @@ has 'reply_markup'; # Array of InlineKeyboardMarkup
sub fields {
return {
'scalar' => [qw/message_id date forward_from_message_id
forward_signature forward_sender_name
forward_date is_automatic_forward edit_date has_protected_content
media_group_id author_signature text caption
new_chat_title delete_chat_photo
group_chat_created supergroup_chat_created
channel_chat_created migrate_to_chat_id
migrate_from_chat_id connected_website/],
'Teapot::Bot::Object::User' => [qw/from forward_from via_bot new_chat_members left_chat_member /],
'scalar' => [qw/message_id message_thread_id date forward_from_message_id
forward_signature forward_sender_name forward_date
is_topic_message is_automatic_forward edit_date
has_protected_content media_group_id author_signature text
caption has_media_spoiler new_chat_title delete_chat_photo
group_chat_created supergroup_chat_created
channel_chat_created migrate_to_chat_id
migrate_from_chat_id connected_website/],
'Teapot::Bot::Object::User' => [qw/from forward_from via_bot new_chat_members left_chat_member /],
'Teapot::Bot::Object::Chat' => [qw/sender_chat chat forward_from_chat/],
'Teapot::Bot::Object::Message' => [qw/reply_to_message pinned_message/],
'Teapot::Bot::Object::MessageEntity' => [qw/entities caption_entities /],
'Teapot::Bot::Object::Chat' => [qw/sender_chat chat forward_from_chat/],
'Teapot::Bot::Object::Message' => [qw/reply_to_message pinned_message/],
'Teapot::Bot::Object::MessageEntity' => [qw/entities caption_entities /],
'Teapot::Bot::Object::Audio' => [qw/audio/],
'Teapot::Bot::Object::Document' => [qw/document/],
'Teapot::Bot::Object::Animation' => [qw/animation/],
'Teapot::Bot::Object::Game' => [qw/game/],
'Teapot::Bot::Object::PhotoSize' => [qw/photo new_chat_photo/],
'Teapot::Bot::Object::Sticker' => [qw/sticker/],
'Teapot::Bot::Object::Video' => [qw/video/],
'Teapot::Bot::Object::Voice' => [qw/voice/],
'Teapot::Bot::Object::VideoNote' => [qw/video_note/],
'Teapot::Bot::Object::Audio' => [qw/audio/],
'Teapot::Bot::Object::Document' => [qw/document/],
'Teapot::Bot::Object::Animation' => [qw/animation/],
'Teapot::Bot::Object::Game' => [qw/game/],
'Teapot::Bot::Object::PhotoSize' => [qw/photo new_chat_photo/],
'Teapot::Bot::Object::Sticker' => [qw/sticker/],
'Teapot::Bot::Object::Video' => [qw/video/],
'Teapot::Bot::Object::Voice' => [qw/voice/],
'Teapot::Bot::Object::VideoNote' => [qw/video_note/],
'Teapot::Bot::Object::Contact' => [qw/contact/],
'Teapot::Bot::Object::Location' => [qw/location/],
'Teapot::Bot::Object::Venue' => [qw/venue/],
'Teapot::Bot::Object::Contact' => [qw/contact/],
'Teapot::Bot::Object::Location' => [qw/location/],
'Teapot::Bot::Object::Venue' => [qw/venue/],
'Teapot::Bot::Object::Poll' => [qw/poll/],
'Teapot::Bot::Object::Poll' => [qw/poll/],
'Teapot::Bot::Object::Invoice' => [qw/invoice/],
'Teapot::Bot::Object::SuccessfulPayment' => [qw/successful_payment/],
'Teapot::Bot::Object::PassportData' => [qw/passport_data/],
'Teapot::Bot::Object::InlineKeyboardMarkup' => [qw/reply_markup/],
'Teapot::Bot::Object::ProximityAlertTriggered' => [qw/proximity_alert_triggered/],
'Teapot::Bot::Object::Dice' => [qw/dice/],
'Teapot::Bot::Object::Invoice' => [qw/invoice/],
'Teapot::Bot::Object::SuccessfulPayment' => [qw/successful_payment/],
'Teapot::Bot::Object::WriteAccessAllowed' => [qw/write_access_allowed/],
'Teapot::Bot::Object::PassportData' => [qw/passport_data/],
'Teapot::Bot::Object::ForumTopicCreated' => [qw/forum_topic_created/],
'Teapot::Bot::Object::ForumTopicEdited' => [qw/forum_topic_edited/],
'Teapot::Bot::Object::ForumTopicClosed' => [qw/forum_topic_closed/],
'Teapot::Bot::Object::ForumTopicReopened' => [qw/forum_topic_reopened/],
'Teapot::Bot::Object::GeneralForumTopicHidden' => [qw/general_forum_topic_unhidden/],
'Teapot::Bot::Object::GeneralForumTopicUnhidden' => [qw/general_forum_topic_unhidden/],
'Teapot::Bot::Object::InlineKeyboardMarkup' => [qw/reply_markup/],
'Teapot::Bot::Object::ProximityAlertTriggered' => [qw/proximity_alert_triggered/],
'Teapot::Bot::Object::Dice' => [qw/dice/],
'Teapot::Bot::Object::MessageAutoDeleteTimerChanged' => [qw/message_auto_delete_timer_changed/],
'Teapot::Bot::Object::VideoChatScheduled' => [qw/voice_chat_scheduled/],
'Teapot::Bot::Object::VideoChatStarted' => [qw/voice_chat_started/],
'Teapot::Bot::Object::VideoChatEnded' => [qw/voice_chat_ended/],
'Teapot::Bot::Object::VideoChatParticipantsInvited' => [qw/voice_chat_participants_invited/],
'Teapot::Bot::Object::WebAppData' => [qw/web_app_data/],
'Teapot::Bot::Object::VideoChatScheduled' => [qw/video_chat_scheduled/],
'Teapot::Bot::Object::VideoChatStarted' => [qw/video_chat_started/],
'Teapot::Bot::Object::VideoChatEnded' => [qw/video_chat_ended/],
'Teapot::Bot::Object::VideoChatParticipantsInvited' => [qw/video_chat_participants_invited/],
'Teapot::Bot::Object::WebAppData' => [qw/web_app_data/],
};
}
@@ -154,48 +179,76 @@ sub arrays {
return qw/photo entities caption_entities new_chat_members new_chat_photo/;
}
sub reply {
my $self = shift;
my $text = shift;
my $chatid = $self->chat->id;
my $ret = '';
if (Teapot::Bot::Object::ChatPermissions->canTalk($self, $chatid)) {
return $self->_brain->sendMessage({chat_id => $chatid, text => $text});
my $send_args->{chat_id} = $self->chat->id;
my $message_thread_id = eval {$self->message_thread_id};
my $can_talk = $self->_brain->can_talk($send_args);
if ($can_talk->{error}) {
$ret = $can_talk;
} else {
if ($can_talk->{can_talk}) {
$send_args->{text} = $text;
if (defined $message_thread_id && $message_thread_id ne '') {
$send_args->{$message_thread_id} = $message_thread_id;
}
$ret = $self->_brain->sendMessage($send_args);
}
}
return {'error' => 1};
return $ret;
}
sub replyMd {
my $self = shift;
my $text = shift;
my $chatid = $self->chat->id;
my $ret = '';
if (Teapot::Bot::Object::ChatPermissions->canTalk ($self, $chatid)) {
my $send_args;
$send_args->{text} = $text;
$send_args->{parse_mode} = 'Markdown';
$send_args->{chat_id} = $chatid;
my $send_args->{chat_id} = $self->chat->id;
my $message_thread_id = eval {$self->message_thread_id};
return $self->_brain->sendMessage($send_args);
my $can_talk = $self->_brain->can_talk($send_args);
if ($can_talk->{error}) {
$ret = $can_talk;
} else {
if ($can_talk->{can_talk}) {
$send_args->{text} = $text;
$send_args->{parse_mode} = 'Markdown';
$send_args->{message_thread_id} = $message_thread_id if (defined $message_thread_id);
$ret = $self->_brain->sendMessage($send_args);
}
}
return {'error' => 1};
return $ret;
}
sub typing {
my $self = shift;
my $chatid = $self->chat->id;
my $ret = '';
my $send_args->{chat_id} = $self->chat->id;
my $can_talk = $self->_brain->can_talk($send_args);
if (Teapot::Bot::Object::ChatPermissions->canTalk ($self, $chatid)) {
return $self->_brain->sendChatAction({chat_id => $chatid});
if ($can_talk->{error}) {
$ret = $can_talk;
} else {
if ($can_talk->{can_talk}) {
$ret = $self->_brain->sendChatAction($send_args);
}
}
return {'error' => 1};
return $ret;
}
1;
@@ -212,7 +265,7 @@ Teapot::Bot::Object::Message - The base class for the Telegram type "Message"
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for the Telegram type "Message".
@@ -228,6 +281,9 @@ A convenience method to reply to a message with text.
Will return the C<Teapot::Bot::Object::Message> object representing the message
sent.
On error returns hash reference with error field set to 1, also set fields
param, url and debug (which is result of Mojo::UserAgent->post->result->json)
=head2 replyMd
@@ -235,12 +291,15 @@ A convenience method to reply to a message with markdown formatted text.
Will return the C<Teapot::Bot::Object::Message> object representing the message
sent.
On error returns hash reference with error field set to 1, also set fields
param, url and debug (which is result of Mojo::UserAgent->post->result->json)
=head2 typing
Sends notification to chat that bot is "typing" something.
Returns nothing that should be checked.
On error returns hash reference with error field set to 1, also set fields
param, url and debug (which is result of Mojo::UserAgent->post->result->json)
=head1 AUTHOR

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::MessageAutoDeleteTimerChanged::VERSION = '0.023';
$Teapot::Bot::Object::MessageAutoDeleteTimerChanged::VERSION = '0.026';
has 'message_auto_delete_time';
@@ -32,7 +32,7 @@ Teapot::Bot::Object::MessageAutoDeleteTimerChanged - The base class for Telegram
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'MessageAutoDeleteTimerChanged' type objects.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::User ();
$Teapot::Bot::Object::MessageEntity::VERSION = '0.023';
$Teapot::Bot::Object::MessageEntity::VERSION = '0.026';
has 'type';
has 'offset';
@@ -39,7 +39,7 @@ Teapot::Bot::Object::MessageEntity - The base class for Telegram 'MessageEntity'
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'MessageEntity' type objects.

View File

@@ -0,0 +1,60 @@
package Teapot::Bot::Object::MessageReaction;
# ABSTRACT: The base class for Telegram message 'Reaction' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::MessageReaction::VERSION = '0.026';
has 'type'; # Type of the reaction, always “emoji”
has 'emoji'; # Reaction emoji. Currently, it can be one of "👍", "👎", "❤", "🔥", "🥰", "👏", "😁", "🤔",
# "🤯", "😱", "🤬", "😢", "🎉", "🤩", "🤮", "💩", "🙏", "👌", "🕊", "🤡", "🥱", "🥴", "😍", "🐳", "❤‍🔥", "🌚", "🌭",
# "💯", "🤣", "⚡", "🍌", "🏆", "💔", "🤨", "😐", "🍓", "🍾", "💋", "🖕", "😈", "😴", "😭", "🤓", "👻", "👨‍💻", "👀",
# "🎃", "🙈", "😇", "😨", "🤝", "✍", "🤗", "🫡", "🎅", "🎄", "☃", "💅", "🤪", "🗿", "🆒", "💘", "🙉", "🦄", "😘",
# "💊", "🙊", "😎", "👾", "🤷‍♂", "🤷", "🤷‍♀", "😡"
has 'custom_emoji_id'; # Only for ReactionTypeCustomEmoji. Custom emoji identifier.
sub fields {
return {
'scalar' => [qw/type emoji custom_emoji_id total_count/],
};
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::MessageReaction - The base class for Telegram message 'Reaction' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Reaction' type.
See L<https://core.telegram.org/bots/api#reactiontype> for details of the
attributes available for C<Teapot::Bot::Object::MessageReaction> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2025 Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,58 @@
package Teapot::Bot::Object::MessageReactionCount;
# ABSTRACT: The base class for Telegram message 'Reaction' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::MessageReaction ();
$Teapot::Bot::Object::MessageReactionCount::VERSION = '0.026';
has 'type'; # Type of the reaction
has 'total_count'; # Number of times the reaction was added
sub fields {
return {
'Teapot::Bot::Object::MessageReaction' => [qw/type/],
'scalar' => [qw/total_count/],
};
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::MessageReactionCount - The base class for Telegram message 'Reaction' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Reaction' type.
See L<https://core.telegram.org/bots/api#reactiontype> for details of the
attributes available for C<Teapot::Bot::Object::MessageReactionCount> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2025 Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,61 @@
package Teapot::Bot::Object::MessageReactionCountUpdated;
# ABSTRACT: The base class for Telegram message 'Reaction' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::MessageReactionCount ();
$Teapot::Bot::Object::MessageReactionCountUpdated::VERSION = '0.026';
has 'chat';
has 'message_id';
has 'date';
has 'reactions';
sub fields {
return {
'Teapot::Bot::Object::Chat' => [qw/chat actor_chat/],
'Teapot::Bot::Object::MessageReactionCount' => [qw/reactions/],
'scalar' => [qw/message_id date/],
};
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::MessageReactionCountUpdated - The base class for Telegram message 'Reaction' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Reaction' type.
See L<https://core.telegram.org/bots/api#reactiontype> for details of the
attributes available for C<Teapot::Bot::Object::MessageReactionCountUpdated> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2025 Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,67 @@
package Teapot::Bot::Object::MessageReactionUpdated;
# ABSTRACT: The base class for Telegram message 'ReactionUpdated' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::Chat ();
use Teapot::Bot::Object::Chat ();
use Teapot::Bot::Object::MessageReaction ();
$Teapot::Bot::Object::MessageReactionUpdated::VERSION = '0.026';
has 'chat'; # The chat containing the message the user reacted to
has 'message_id'; # Unique identifier of the message inside the chat
has 'user'; # Optional. The user that changed the reaction, if the user isn't anonymous
has 'actor_chat'; # Optional. The chat on behalf of which the reaction was changed, if the user is anonymous
has 'date'; # Date of the change in Unix time
has 'old_reaction'; # Previous list of reaction types that were set by the user
has 'new_reaction'; # New list of reaction types that have been set by the user
sub fields {
return {
'Teapot::Bot::Object::Chat' => [qw/chat actor_chat/],
'Teapot::Bot::Object::User' => [qw/user/],
'Teapot::Bot::Object::MessageReaction' => [qw/old_reaction new_reaction/],
'scalar' => [qw/message_id date/],
};
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::MessageReaction - The base class for Telegram message 'ReactionUpdated' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ReactionUpdated' type.
See L<https://core.telegram.org/bots/api#messagereactionupdated> for details of the
attributes available for C<Teapot::Bot::Object::MessageReactionUpdated> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2025 Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -10,7 +10,7 @@ use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::EncryptedPassportElement ();
use Teapot::Bot::Object::EncryptedCredentials ();
$Teapot::Bot::Object::PassportData::VERSION = '0.023';
$Teapot::Bot::Object::PassportData::VERSION = '0.026';
has 'data'; # Array of EncryptedPassportElement
has 'credentials'; # EncryptedCredentials
@@ -40,7 +40,7 @@ Teapot::Bot::Object::PassportData - The base class for Telegram 'PassportData' t
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'PassportData' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::PhotoSize::VERSION = '0.023';
$Teapot::Bot::Object::PhotoSize::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
@@ -34,7 +34,7 @@ Teapot::Bot::Object::PhotoSize - The base class for Telegram message 'PhotoSize'
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'PhotoSize' type.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PollOption ();
$Teapot::Bot::Object::Poll::VERSION = '0.023';
$Teapot::Bot::Object::Poll::VERSION = '0.026';
has 'id';
has 'question';
@@ -52,7 +52,7 @@ Teapot::Bot::Object::Poll - The base class for Telegram 'Poll' type objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Poll' type objects

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::PollAnswer::VERSION = '0.023';
$Teapot::Bot::Object::PollAnswer::VERSION = '0.026';
has 'poll_id';
has 'user';
@@ -39,7 +39,7 @@ Teapot::Bot::Object::PollAnswer - The base class for Telegram 'PollAnswer' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'PollAnswer' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::PollOption::VERSION = '0.023';
$Teapot::Bot::Object::PollOption::VERSION = '0.026';
has 'text';
has 'voter_count';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::PollOption - The base class for Telegram 'PollOption' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'PollOption' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::ProximityAlertTriggered::VERSION = '0.023';
$Teapot::Bot::Object::ProximityAlertTriggered::VERSION = '0.026';
has 'traveler'; # User
has 'watcher'; # User
@@ -35,7 +35,7 @@ Teapot::Bot::Object::ProximityAlertTriggered - The base class for Telegram messa
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'ProximityAlertTriggered' type.

View File

@@ -10,24 +10,30 @@ use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PhotoSize ();
use Teapot::Bot::Object::MaskPosition ();
$Teapot::Bot::Object::Sticker::VERSION = '0.023';
$Teapot::Bot::Object::Sticker::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
has 'type';
has 'width';
has 'height';
has 'is_animated';
has 'is_video';
has 'thumb'; # PhotoSize
has 'thumbnail'; # PhotoSize
has 'emoji';
has 'set_name';
has 'premium_animation';
has 'mask_position';
has 'custom_emoji_id';
has 'needs_repainting';
has 'file_size';
sub fields {
return {
scalar => [ qw/file_id file_unique_id width height is_animated is_video emoji set_name file_size/ ],
'Teapot::Bot::Object::PhotoSize' => [ qw/thumb/ ],
scalar => [ qw/file_id file_unique_id type width height is_animated is_video emoji
set_name premium_animation custom_emoji_id needs_repainting
file_size/ ],
'Teapot::Bot::Object::PhotoSize' => [ qw/thumbnail/ ],
'Teapot::Bot::Object::MaskPosition' => [qw/mask_position/],
};
}
@@ -46,7 +52,7 @@ Teapot::Bot::Object::Sticker - The base class for Telegram message 'Sticker' typ
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'Sticker' type.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
# use Teapot::Bot::Object::OrderInfo;
$Teapot::Bot::Object::SuccessfulPayment::VERSION = '0.023';
$Teapot::Bot::Object::SuccessfulPayment::VERSION = '0.026';
has 'currency';
has 'total_amount';
@@ -41,7 +41,7 @@ Teapot::Bot::Object::SuccessfulPayment - The base class for Telegram 'Successful
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'SuccessfulPayment' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::User::VERSION = '0.023';
$Teapot::Bot::Object::User::VERSION = '0.026';
has 'id';
has 'is_bot';
@@ -41,7 +41,7 @@ Teapot::Bot::Object::User - The base class for Telegram message 'User' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'User' type.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::Location ();
$Teapot::Bot::Object::Venue::VERSION = '0.023';
$Teapot::Bot::Object::Venue::VERSION = '0.026';
has 'location'; #Location
has 'title';
@@ -41,7 +41,7 @@ Teapot::Bot::Object::Venue - The base class for Telegram 'LoginUrl' type objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'LoginUrl' type objects.

View File

@@ -9,14 +9,14 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PhotoSize ();
$Teapot::Bot::Object::Video::VERSION = '0.023';
$Teapot::Bot::Object::Video::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
has 'width';
has 'height';
has 'duration';
has 'thumb'; # Optional. Video thumbnail
has 'thumbnail'; # Optional. Video thumbnail
has 'file_name'; # Optional. Original filename as defined by sender
has 'mime_type'; # Optional. Mime type of a file as defined by sender
has 'file_size'; # Optional. File size
@@ -25,7 +25,7 @@ sub fields {
return {
scalar => [qw/file_id file_unique_id width height duration file_name mime_type
file_size/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumb/],
'Teapot::Bot::Object::PhotoSize' => [qw/thumbnail/],
};
}
@@ -43,7 +43,7 @@ Teapot::Bot::Object::Video - The base class for Telegram 'Video' object
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Video' object.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::VideoChatEnded::VERSION = '0.023';
$Teapot::Bot::Object::VideoChatEnded::VERSION = '0.026';
# This object represents a service message about a Video chat started in the chat. Currently holds no information.
has 'duration';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::VideoChatEnded - The base class for Telegram message 'Video
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'VideoChatEnded' type.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::User ();
$Teapot::Bot::Object::VideoChatParticipantsInvited::VERSION = '0.023';
$Teapot::Bot::Object::VideoChatParticipantsInvited::VERSION = '0.026';
# This object represents a service message about a Video chat started in the chat. Currently holds no information.
has 'users';
@@ -36,7 +36,7 @@ Teapot::Bot::Object::VideoChatParticipantsInvited - The base class for Telegram
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'VideoChatParticipantsInvited' type.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::VideoChatScheduled::VERSION = '0.023';
$Teapot::Bot::Object::VideoChatScheduled::VERSION = '0.026';
# This object represents a service message about a video chat started in the chat. Currently holds no information.
has 'start_date';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::VideoChatScheduled - The base class for Telegram message 'V
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'VideoChatScheduled' type.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::VideoChatStarted::VERSION = '0.023';
$Teapot::Bot::Object::VideoChatStarted::VERSION = '0.026';
# This object represents a service message about a Video chat started in the chat. Currently holds no information.
has 'stub';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::VideoChatStarted - The base class for Telegram message 'Vid
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'VideoChatStarted' type.

View File

@@ -9,7 +9,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
use Teapot::Bot::Object::PhotoSize ();
$Teapot::Bot::Object::VideoNote::VERSION = '0.023';
$Teapot::Bot::Object::VideoNote::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
@@ -40,7 +40,7 @@ Teapot::Bot::Object::VideoNote - The base class for Telegram 'VideoNote' type ob
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'VideoNote' type object.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::Voice::VERSION = '0.023';
$Teapot::Bot::Object::Voice::VERSION = '0.026';
has 'file_id';
has 'file_unique_id';
@@ -34,7 +34,7 @@ Teapot::Bot::Object::Voice - The base class for Telegram 'Voice' type objects
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'Voice' type objects.

View File

@@ -8,7 +8,7 @@ use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::WebAppData::VERSION = '0.023';
$Teapot::Bot::Object::WebAppData::VERSION = '0.026';
has 'data';
has 'button_text';
@@ -31,7 +31,7 @@ Teapot::Bot::Object::WebAppData - The base class for Telegram 'WebAppData' type
=head1 VERSION
version 0.023
version 0.026
=head1 DESCRIPTION
The base class for Telegram 'WebAppData' type objects.

View File

@@ -0,0 +1,51 @@
package Teapot::Bot::Object::WriteAccessAllowed;
# ABSTRACT: The base class for Telegram message 'WriteAccessAllowed' type.
use strict;
use warnings;
use 5.018; ## no critic (ProhibitImplicitImport)
use utf8;
use Mojo::Base 'Teapot::Bot::Object::Base';
$Teapot::Bot::Object::WriteAccessAllowed::VERSION = '0.026';
sub fields {
return { };
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Teapot::Bot::Object::WriteAccessAllowed - The base class for Telegram message 'WriteAccessAllowed' type
=head1 VERSION
version 0.026
=head1 DESCRIPTION
The base class for Telegram message 'WriteAccessAllowed' type.
See L<https://core.telegram.org/bots/api#writeaccessallowed> for details of the
attributes available for C<Teapot::Bot::Object::WriteAccessAllowed> objects.
=head1 AUTHOR
Sergei Fedosov <eleksir@gmail.com>
=head1 LICENSE AND COPYRIGHT
This software is copyright (c) 2023 by Sergei Fedosov <eleksir@gmail.com>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -5,20 +5,21 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use strict;
use warnings;
use utf8;
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use Clone qw (clone);
use Data::Dumper qw (Dumper);
use Log::Any qw ($log);
use Clone qw (clone);
use Data::Dumper qw (Dumper);
use Log::Any qw ($log);
use Math::Random::Secure qw (irand);
use Mojo::Base 'Teapot::Bot::Brain';
use Mojo::Base 'Teapot::Bot::Brain';
use BotLib::Admin qw (FortuneToggleList ChanMsgEnabled GreetMsgEnabled GoodbyeMsgEnabled);
use BotLib qw (Command Highlight BotSleep IsCensored);
use BotLib::Conf qw (LoadConf);
use BotLib::Util qw (trim fmatch);
use RedisLib qw (redis_events_listener);
use BotLib::Admin qw (FortuneToggleList ChanMsgEnabled GreetMsgEnabled GoodbyeMsgEnabled
MigrateSettingsToNewChatID IsCensored GetSlackGreetRememberId SetSlackGreetRememberId);
use BotLib qw (Command);
use BotLib::Conf qw (LoadConf);
use BotLib::Util qw (trim fmatch Highlight RandomCommonPhrase);
use RedisLib qw (redis_events_listener);
use version; our $VERSION = qw (1.0);
use Exporter qw (import);
@@ -41,12 +42,14 @@ my @introduce_greet = (
'Наше вам с кисточкой тут, на канальчике',
);
my $redismsg->{from} = 'telegram';
$redismsg->{plugin} = 'telegram';
$redismsg->{misc}->{answer} = 1;
$redismsg->{misc}->{csign} = "$c->{telegrambot}->{csign}";
$redismsg->{misc}->{msg_format} = 0;
$redismsg->{misc}->{fwd_cnt} = 1;
my $redismsg->{from} = 'telegram';
$redismsg->{threadid} = '';
$redismsg->{plugin} = 'telegram';
$redismsg->{misc}->{answer} = 1;
$redismsg->{misc}->{csign} = "$c->{telegrambot}->{csign}";
$redismsg->{misc}->{msg_format} = 0;
$redismsg->{misc}->{fwd_cnt} = 1;
$redismsg->{misc}->{good_morning} = 0;
sub __cron {
my $self = shift;
@@ -56,7 +59,6 @@ sub __cron {
# Press 'F' to pay respect
$rmsg->{message} = sprintf '%sf', $c->{telegrambot}->{csign};
$rmsg->{misc}->{msg_format} = 1;
$rmsg->{misc}->{answer} = 1;
$rmsg->{misc}->{good_morning} = 1;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time);
@@ -80,11 +82,14 @@ sub __cron {
$rmsg->{userid} = "$enabledfortunechat";
$rmsg->{chatid} = "$enabledfortunechat";
$log->debug ('Outgoing redis message: ' . Dumper ($rmsg));
$log->debug ('[DEBUG] Outgoing redis message: ' . Dumper ($rmsg));
$pubsub->json ($c->{'redis_router_channel'})->notify (
$c->{'redis_router_channel'} => $rmsg,
);
# Ratelimit this, just in case
sleep (1);
}
}
@@ -95,6 +100,15 @@ sub __on_msg {
my ($self, $msg) = @_;
# chat info
my $chatid;
# Ловим сообщения о миграции.
if ($msg->can ('migrate_to_chat_id') && $msg->can ('migrate_from_chat_id')) {
if (defined ($msg->migrate_from_chat_id) && defined ($msg->migrate_from_chat_id)) {
MigrateSettingsToNewChatID ($msg->migrate_from_chat_id, $msg->migrate_to_chat_id);
return;
}
}
my $chatname = 'Noname chat';
# user sending message info
my ($userid, $username, $fullname, $highlight, $vis_a_vi) = Highlight ($msg);
@@ -103,15 +117,15 @@ sub __on_msg {
unless ($myid) {
my $myObj = $self->getMe ();
unless ($myObj) {
$log->error ('[ERROR] Unable to get chatid, API Error?');
if ($myObj->{error}) {
$log->error ("[ERROR] Unable to call getMe BotAPI method: " . Dumper ($myObj));
return;
}
$myid = $myObj->id;
$myusername = $myObj->username;
$myid = $myObj->id;
$myusername = $myObj->username;
$myfirst_name = $myObj->first_name;
$mylast_name = $myObj->last_name;
$mylast_name = $myObj->last_name;
if (defined ($myfirst_name) && ($myfirst_name ne '') && defined ($mylast_name) && ($mylast_name ne '')) {
$myfullname = $myfirst_name . ' ' . $mylast_name;
@@ -174,6 +188,10 @@ sub __on_msg {
}
}
# Remember user id, to check it later, in next event.
SetSlackGreetRememberId ($member->id);
$log->info (sprintf ("[INFO] New chat member %s (%s)", $member_str, $member->id));
push @members, sprintf '[%s](tg://user?id=%s)', $member_str, $member->id;
}
@@ -193,8 +211,55 @@ sub __on_msg {
);
}
BotSleep $msg;
$msg->replyMd ($phrase);
sleep 2;
my $m->{text} = $phrase;
$m->{chat_id} = $chatid;
$m->{disable_notification} = 1;
$m->{parse_mode} = 'Markdown';
my $res = $main::TGM->sendMessage ($m);
if ($res->{error}) {
$log->error ("[ERROR] Unable to call sendMessage BotAPI method: " . Dumper ($res));
}
return;
}
# Bots have non-0% tendency to appear with this kind of updates.
if ($msg->can ('new_chat_member') && defined ($msg->new_chat_member)) {
return;
# slackware_ru
unless ($chatid == -1001332512695) {
return;
}
sleep 2;
# Can be racy, but hope for best.
unless (GetSlackGreetRememberId($msg->new_chat_member->user->id)) {
$log->info ("[INFO] $vis_a_vi entered to chat without prior notice event");
# if earlier we did not get new chat member event, it looks like we have spammer here. Ban it.
my $send_args = undef;
$send_args->{chat_id} = 0 + $chatid;
$send_args->{user_id} = 0 + $msg->new_chat_member->user->id;
# According to docs if user banned for <30 seonds || >366 days it banned forever.
# Some bots have annoying behavior to constantly try to join chat.
$send_args->{until_date} = 10 + time ();
$send_args->{until_date} = 10 + time ();
my $result = $self->banChatMember ($send_args);
if ((ref ($result) eq 'JSON::PP::Boolean') && ($result == JSON::PP::true)) {
$log->info ("[INFO] $vis_a_vi banned");
return;
} else {
$log->error ("[ERROR] Unable to ban user: " . Dumper ($result));
}
}
return;
}
@@ -237,7 +302,17 @@ sub __on_msg {
my $leftuser = sprintf '[%s](tg://user?id=%s)', $member_str, $member->id;
$phrase = sprintf $goodbye[irand ($#goodbye + 1)], $leftuser;
$msg->replyMd ($phrase);
my $res = $msg->replyMd ($phrase);
if ($res->{error}) {
$log->error ("[ERROR] Unable to call sendMessage BotAPI method: " . Dumper ($res));
}
return;
}
if ($msg->can ('old_reaction') || $msg->can ('new_reaction')) {
$log->debug (sprintf ('[DEBUG] user %s in %s posts reaction to message', $vis_a_vi, $chatname));
return;
}
@@ -271,7 +346,7 @@ sub __on_msg {
} while (sleep 1);
}
$log->debug ('Outgoing redis message: ' . Dumper ($rmsg));
$log->debug ('[DEBUG] Outgoing redis message: ' . Dumper ($rmsg));
my $pubsub = $main::REDIS->pubsub;
@@ -297,19 +372,27 @@ sub __on_msg {
# И вот тут как раз такая проверка и происходит
if (IsCensored $msg) {
$log->info (sprintf '[INFO] In public chat %s (%s) message from %s was censored', $chatname, $chatid, $vis_a_vi);
$self->deleteMessage ({chat_id => $chatid, message_id => $msg->{message_id}});
my $res = $self->deleteMessage ({chat_id => $chatid, message_id => $msg->{message_id}});
if ($res->{error}) {
$log->error ("[ERROR] Unable to call sendMessage BotAPI method: " . Dumper ($res));
}
}
# Некоторые рекламные товарищи пытаются срать своими каналами в чятик это тоже можно зацензурить ботом и это
# пидорство он будет удалять asap
# 136817688 - это специальный id пользователя, который принимает облик канала, на него можно нажать и попасть
# на рекламируемый канал
# на рекламируемый канал
if ($msg->from->id == 136817688) {
my $sender_chat = eval { $msg->sender_chat->id };
if (defined $sender_chat) {
unless (ChanMsgEnabled ($chatid)) {
$self->deleteMessage ({chat_id => $chatid, message_id => $msg->{message_id}});
my $res = $self->deleteMessage ({chat_id => $chatid, message_id => $msg->{message_id}});
if ($res->{error}) {
$log->error ("[ERROR] Unable to call sendMessage BotAPI method: " . Dumper ($res));
}
}
}
}
@@ -360,6 +443,15 @@ sub __on_msg {
$rmsg->{chatid} = "$chatid";
$rmsg->{misc}->{answer} = 1;
# Если тип группы supergroup, в ней могут быть треды, попробуем найти message_thread_id, если таковой есть
if ($msg->chat->type eq 'supergroup') {
if ($msg->can ('is_topic_message') && $msg->is_topic_message) {
if ($msg->can ('message_thread_id') && $msg->message_thread_id ne '') {
$rmsg->{threadid} = "$msg->message_thread_id";
}
}
}
{
do {
my $ready = eval { $main::RCONN->is_connected; };
@@ -367,7 +459,7 @@ sub __on_msg {
} while (sleep 1);
}
$log->debug ('Outgoing redis message: ' . Dumper ($rmsg));
$log->debug ('[DEBUG] Outgoing redis message: ' . Dumper ($rmsg));
my $pubsub = $main::REDIS->pubsub;
@@ -400,6 +492,15 @@ sub __on_msg {
$rmsg->{chatid} = "$chatid";
$rmsg->{misc}->{answer} = 1;
# Если тип группы supergroup, в ней могут быть треды, попробуем найти message_thread_id, если таковой есть
if ($msg->chat->type eq 'supergroup') {
if ($msg->can ('is_topic_message') && $msg->is_topic_message) {
if ($msg->can ('message_thread_id') && $msg->message_thread_id ne '') {
$rmsg->{threadid} = "$msg->message_thread_id";
}
}
}
# Сообщение обращено к боту
if ((lc ($text) =~ /^${qname}[\,|\:]? (.+)/) or (lc ($text) =~ /^${qtname}[\,|\:]? (.+)/)){
$phrase = $1;
@@ -425,7 +526,7 @@ sub __on_msg {
} while (sleep 1);
}
$log->debug ('Outgoing redis message: ' . Dumper ($rmsg));
$log->debug ('[DEBUG] Outgoing redis message: ' . Dumper ($rmsg));
my $pubsub = $main::REDIS->pubsub;
@@ -452,10 +553,25 @@ sub __on_msg {
$msg->typing ();
sleep (irand 2);
$log->debug (sprintf ('[DEBUG] In public chat %s (%s) bot reply to %s: %s', $chatname, $chatid, $vis_a_vi, $reply));
$msg->reply ($reply);
$log->debug (
sprintf (
'[DEBUG] In public chat %s (%s) bot reply to %s: %s',
$chatname, $chatid, $vis_a_vi, $reply,
)
);
my $res = $msg->reply ($reply);
if ($res->{error}) {
$log->error ("[ERROR] Unable to call sendMessage BotAPI method: " . Dumper ($res));
}
} else {
$log->debug (sprintf ('[DEBUG] In public chat %s (%s) bot is not required to reply to %s', $chatname, $chatid, $vis_a_vi));
$log->debug (
sprintf (
'[DEBUG] In public chat %s (%s) bot is not required to reply to %s',
$chatname, $chatid, $vis_a_vi,
)
);
}
# should be channel, so we can't talk
@@ -466,23 +582,6 @@ sub __on_msg {
return;
}
sub RandomCommonPhrase {
my @myphrase = (
'Так, блядь...',
'*Закатывает рукава* И ради этого ты меня позвал?',
'Ну чего ты начинаешь, нормально же общались',
'Повтори свой вопрос, не поняла',
'Выйди и зайди нормально',
'Я подумаю',
'Даже не знаю, что на это ответить',
'Ты упал такие вопросы девочке задавать?',
'Можно и так, но не уверена',
'А как ты думаешь?',
'А ви, таки, почему интересуетесь?',
);
return $myphrase[irand ($#myphrase + 1)];
}
# setup our bot
sub init {
@@ -502,6 +601,8 @@ sub RunTelegramBot {
eval { ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
TelegramBot->new->think;
};
$log->error ("[ERROR] $EVAL_ERROR");
}
return;

View File

@@ -0,0 +1,101 @@
#!/usr/bin/perl
# По соглашению, рабочим каталогом является каталог с приложением, то есть запускать этот скрипт надо как
# util/get_chat_users_by_id.pl
# Для запуска этого скрипта нам понадобится список id пользователей и id чата, для которого мы дампим информацию о
# пользователях. Входные данные мы складываем в файл get_chat_users_by_id.json.
# На выходе в каталоге c->{cachedir} мы получим каталог get_chat_users_by_id/chat_id с кучей json-чиков, по одному на
# каждого пользователя.
use 5.018; ## no critic (ProhibitImplicitImport)
use strict;
use warnings;
use utf8;
use open qw (:std :utf8);
use English qw ( -no_match_vars );
use version; our $VERSION = qw (1.0);
my $workdir;
# before we run, change working dir
BEGIN {
use Cwd qw (chdir abs_path);
my @CWD = split /\//xms, abs_path ($PROGRAM_NAME);
if ($#CWD > 1) {
$#CWD = $#CWD - 2;
}
$workdir = join '/', @CWD;
chdir $workdir;
}
use lib ("$workdir/lib", "$workdir/vendor_perl", "$workdir/vendor_perl/lib/perl5");
no Cwd;
use Cwd::utf8 qw (chdir abs_path);
use Data::Dumper qw (Dumper);
use File::Path qw(make_path);
use JSON::XS qw (decode_json);
# Грех не воспользоваться, если у нас оно есть под рукой, хотя по мне тут достаточно Http::Tiny
use Mojo::UserAgent ();
use BotLib::Conf qw (LoadConf);
# Поехали!
my $c = LoadConf ();
# Пример содержимого файлика: {"chatid": -номер, "users": ["id1", "id2"]}
my $data = LoadConf ("$workdir/get_chat_users_by_id.json");
unless (defined $c->{cachedir}) {
die "Please define cachedir in data/config.jason\n";
}
unless (defined $data->{chatid}) {
die "Please define chatid in get_chat_users_by_id.json\n";
}
my $targetDir = sprintf ("%s/get_chat_users_by_id/%s", $c->{cachedir}, $data->{chatid});
make_path ($targetDir) or die "Unable to create $targetDir: $OS_ERROR\n";
my $url = sprintf (
"https://api.telegram.org/bot%s/getChatMember",
$c->{telegrambot}->{token},
);
while (my $userid = pop @{$data->{users}}) {
my $ua = Mojo::UserAgent->new->connect_timeout (3);
my $r = $ua->post ($url => form => {chat_id => $data->{chatid}, user_id => $userid} )->result;
# Do not flood api with requests!
sleep 1;
unless ($r->is_success) {
warn sprintf ("Unable to get info about user: http status %s\n%s\n", $r->{code}, $r->body);
next;
}
my $j = eval { decode_json ($r->body) };
if (defined $j) {
my $jobj = JSON::XS->new ();
$jobj = $jobj->pretty (1);
$jobj = $jobj->canonical (1);
my $json = $jobj->encode ($j);
my $file = sprintf "%s/%s.json", $targetDir, $userid;
open (my $FH, '>', $file) or do {
warn "Unable to write to $file: $OS_ERROR\n";
next;
};
print $FH $json . "\n";
close $FH;
} else {
warn sprintf ("Telegram bot api returns corrupted json: %s,\n%s\n", $r->body, $@);
}
}