2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-08 13:24:56 +00:00

50_TelegramBot.pm: V1.6 customization for messages, supergroups, description favorites

git-svn-id: https://svn.fhem.de/fhem/trunk@11290 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
viegener 2016-04-21 13:24:27 +00:00
parent fa4a59f555
commit f9592bfb67
2 changed files with 311 additions and 181 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 50_TelegramBot supergroup support description for favorites
text customization for response messages
- feature: 49_SSCam: motion detection parameters can be entered if
motion detection by camera or by SVS is used
- feature: 74_AMAD: Flowinstaller and FlowsetUpdater - Support for older

View File

@ -18,7 +18,7 @@
# along with Fhem. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
#
#
# TelegramBot (c) Johannes Viegener / https://github.com/viegener/Telegram-fhem
#
# This module handles receiving and sending messages to the messaging service telegram (see https://telegram.org/)
@ -112,19 +112,30 @@
# fix contact update
# 1.5 2016-03-19 retry for send / confirmation
#
# supergroups added now also to contacts
# fix for first name of contacts undefined
# remove stale/duplicate contacts (based on username) on update of contacts (supergroups get new ids)
# Allow localization outward facing messages -> templates with replacements (German as default)
# New attributes for visible telegram responses:
# textResponseConfirm, textResponseFavorites, textResponseCommands, textResponseResult, textResponseUnauthorized
# descriptions for favorites can be specified (enclosed in [])
# descriptions are shown in favorite list and confirmation dialogue
# texts are converted to UTF8 also for keyboards
# favorite list corrected
# 1.6 2016-04-08 text customization for replies/messages and favorite descriptions
#
#
#
##############################################################################
# TASKS
#
# Look for solution on space at beginning of line --> checked that data is sent correctly to telegram but does not end up in the message
#
# allow keyboards in the device api
#
# dialog function
#
# Correct Readings times for prev Msg?
#
##############################################################################
# Ideas / Future
# add replyTo
@ -136,7 +147,7 @@ package main;
use strict;
use warnings;
use HttpUtils;
#use HttpUtils;
use Encode;
use JSON;
@ -156,35 +167,40 @@ sub TelegramBot_Callback($$$);
sub TelegramBot_SendIt($$$$$;$);
sub TelegramBot_checkAllowedPeer($$$);
sub TelegramBot_SplitFavoriteDef($$);
sub TelegramBot_GetUTF8Back( $ );
sub TelegramBot_PutToUTF8( $ );
#########################
# Globals
my %sets = (
"message" => "textField",
"msg" => "textField",
"send" => "textField",
"message" => "textField",
"msg" => "textField",
"send" => "textField",
"sendImage" => "textField",
"sendPhoto" => "textField",
"sendImage" => "textField",
"sendPhoto" => "textField",
"sendDocument" => "textField",
"sendMedia" => "textField",
"sendVoice" => "textField",
"sendDocument" => "textField",
"sendMedia" => "textField",
"sendVoice" => "textField",
"replaceContacts" => "textField",
"reset" => undef,
"replaceContacts" => "textField",
"reset" => undef,
"zDebug" => "textField"
"zDebug" => "textField"
);
my %deprecatedsets = (
"image" => "textField",
"sendPhoto" => "textField",
"image" => "textField",
"sendPhoto" => "textField",
);
my %gets = (
"urlForFile" => "textField"
"urlForFile" => "textField"
);
my $TelegramBot_header = "agent: TelegramBot/1.0\r\nUser-Agent: TelegramBot/1.0\r\nAccept: application/json\r\nAccept-Charset: utf-8";
@ -223,22 +239,23 @@ my %TelegramBot_hu_do_params = (
# define functions and attributed for the module and corresponding devices
sub TelegramBot_Initialize($) {
my ($hash) = @_;
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
require "$attr{global}{modpath}/FHEM/DevIo.pm";
$hash->{DefFn} = "TelegramBot_Define";
$hash->{UndefFn} = "TelegramBot_Undef";
$hash->{StateFn} = "TelegramBot_State";
$hash->{GetFn} = "TelegramBot_Get";
$hash->{SetFn} = "TelegramBot_Set";
$hash->{AttrFn} = "TelegramBot_Attr";
$hash->{AttrList} = "defaultPeer defaultPeerCopy:0,1 pollingTimeout cmdKeyword cmdSentCommands favorites:textField-long cmdFavorites cmdRestrictedPeer cmdTriggerOnly:0,1 saveStateOnContactChange:1,0 maxFileSize maxReturnSize cmdReturnEmptyResult:1,0 pollingVerbose:1_Digest,2_Log,0_None allowUnknownContacts:1,0 maxRetries:0,1,2,3,4,5 ".
$readingFnAttributes;
$hash->{DefFn} = "TelegramBot_Define";
$hash->{UndefFn} = "TelegramBot_Undef";
$hash->{StateFn} = "TelegramBot_State";
$hash->{GetFn} = "TelegramBot_Get";
$hash->{SetFn} = "TelegramBot_Set";
$hash->{AttrFn} = "TelegramBot_Attr";
$hash->{AttrList} = "defaultPeer defaultPeerCopy:0,1 pollingTimeout cmdKeyword cmdSentCommands favorites:textField-long cmdFavorites cmdRestrictedPeer ". "cmdTriggerOnly:0,1 saveStateOnContactChange:1,0 maxFileSize maxReturnSize cmdReturnEmptyResult:1,0 pollingVerbose:1_Digest,2_Log,0_None ".
"allowUnknownContacts:1,0 textResponseConfirm:textField textResponseCommands:textField ".
"textResponseFavorites:textField textResponseResult:textField textResponseUnauthorized:textField ".
" maxRetries:0,1,2,3,4,5 ".$readingFnAttributes;
}
######################################
# Define function is called for actually defining a device of the corresponding module
# For TelegramBot this is mainly API id for the bot
@ -290,7 +307,7 @@ sub TelegramBot_Define($$) {
#####################################
# Undef function is corresponding to the delete command the opposite to the define function
# Cleanup the device specifically for external ressources like connections, open files,
# external memory outside of hash, sub processes and timers
# external memory outside of hash, sub processes and timers
sub TelegramBot_Undef($$)
{
my ($hash, $arg) = @_;
@ -322,53 +339,53 @@ sub TelegramBot_Undef($$)
####################################
# State function to ensure contacts internal hash being reset on Contacts Readings Set
sub TelegramBot_State($$$$) {
my ($hash, $time, $name, $value) = @_;
my ($hash, $time, $name, $value) = @_;
# Log3 $hash->{NAME}, 4, "TelegramBot_State called with :$name: value :$value:";
if ($name eq 'Contacts') {
TelegramBot_CalcContactsHash( $hash, $value );
Log3 $hash->{NAME}, 4, "TelegramBot_State Contacts hash has now :".scalar(keys %{$hash->{Contacts}}).":";
}
return undef;
}
return undef;
}
####################################
# set function for executing set operations on device
sub TelegramBot_Set($@)
{
my ( $hash, $name, @args ) = @_;
my ( $hash, $name, @args ) = @_;
Log3 $name, 4, "TelegramBot_Set $name: called ";
### Check Args
my $numberOfArgs = int(@args);
return "TelegramBot_Set: No cmd specified for set" if ( $numberOfArgs < 1 );
### Check Args
my $numberOfArgs = int(@args);
return "TelegramBot_Set: No cmd specified for set" if ( $numberOfArgs < 1 );
my $cmd = shift @args;
my $cmd = shift @args;
Log3 $name, 4, "TelegramBot_Set $name: Processing TelegramBot_Set( $cmd )";
if (!exists($sets{$cmd})) {
my @cList;
foreach my $k (keys %sets) {
my $opts = undef;
$opts = $sets{$k};
if (!exists($sets{$cmd})) {
my @cList;
foreach my $k (keys %sets) {
my $opts = undef;
$opts = $sets{$k};
if (defined($opts)) {
push(@cList,$k . ':' . $opts);
} else {
push (@cList,$k);
}
} # end foreach
if (defined($opts)) {
push(@cList,$k . ':' . $opts);
} else {
push (@cList,$k);
}
} # end foreach
return "TelegramBot_Set: Unknown argument $cmd, choose one of " . join(" ", @cList);
} # error unknown cmd handling
return "TelegramBot_Set: Unknown argument $cmd, choose one of " . join(" ", @cList);
} # error unknown cmd handling
my $ret = undef;
if( ($cmd eq 'message') || ($cmd eq 'msg') || ($cmd =~ /^send.*/ ) ) {
if( ($cmd eq 'message') || ($cmd eq 'msg') || ($cmd =~ /^send.*/ ) ) {
return "TelegramBot_Set: Command $cmd, no peers and no text/file specified" if ( $numberOfArgs < 2 );
@ -461,39 +478,39 @@ sub TelegramBot_Set($@)
# get function for gaining information from device
sub TelegramBot_Get($@)
{
my ( $hash, $name, @args ) = @_;
my ( $hash, $name, @args ) = @_;
Log3 $name, 5, "TelegramBot_Get $name: called ";
### Check Args
my $numberOfArgs = int(@args);
return "TelegramBot_Get: No value specified for get" if ( $numberOfArgs < 1 );
### Check Args
my $numberOfArgs = int(@args);
return "TelegramBot_Get: No value specified for get" if ( $numberOfArgs < 1 );
my $cmd = $args[0];
my $cmd = $args[0];
my $arg = ($args[1] ? $args[1] : "");
Log3 $name, 5, "TelegramBot_Get $name: Processing TelegramBot_Get( $cmd )";
if(!exists($gets{$cmd})) {
my @cList;
foreach my $k (sort keys %gets) {
my $opts = undef;
$opts = $sets{$k};
if(!exists($gets{$cmd})) {
my @cList;
foreach my $k (sort keys %gets) {
my $opts = undef;
$opts = $sets{$k};
if (defined($opts)) {
push(@cList,$k . ':' . $opts);
} else {
push (@cList,$k);
}
} # end foreach
if (defined($opts)) {
push(@cList,$k . ':' . $opts);
} else {
push (@cList,$k);
}
} # end foreach
return "TelegramBot_Get: Unknown argument $cmd, choose one of " . join(" ", @cList);
} # error unknown cmd handling
return "TelegramBot_Get: Unknown argument $cmd, choose one of " . join(" ", @cList);
} # error unknown cmd handling
my $ret = undef;
if($cmd eq 'urlForFile') {
if($cmd eq 'urlForFile') {
if ( $numberOfArgs != 2 ) {
return "TelegramBot_Get: Command $cmd, no file id specified";
}
@ -530,36 +547,36 @@ sub TelegramBot_Get($@)
##############################
# attr function for setting fhem attributes for the device
sub TelegramBot_Attr(@) {
my ($cmd,$name,$aName,$aVal) = @_;
my $hash = $defs{$name};
my ($cmd,$name,$aName,$aVal) = @_;
my $hash = $defs{$name};
Log3 $name, 5, "TelegramBot_Attr $name: called ";
return "\"TelegramBot_Attr: \" $name does not exist" if (!defined($hash));
return "\"TelegramBot_Attr: \" $name does not exist" if (!defined($hash));
if (defined($aVal)) {
Log3 $name, 5, "TelegramBot_Attr $name: $cmd on $aName to $aVal";
} else {
Log3 $name, 5, "TelegramBot_Attr $name: $cmd on $aName to <undef>";
}
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
if ($cmd eq "set") {
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
if ($cmd eq "set") {
if ($aName eq 'defaultPeer') {
$attr{$name}{'defaultPeer'} = $aVal;
$attr{$name}{'defaultPeer'} = $aVal;
} elsif ($aName eq 'cmdKeyword') {
$attr{$name}{'cmdKeyword'} = $aVal;
} elsif ($aName eq 'cmdKeyword') {
$attr{$name}{'cmdKeyword'} = $aVal;
} elsif ($aName eq 'cmdSentCommands') {
$attr{$name}{'cmdSentCommands'} = $aVal;
} elsif ($aName eq 'cmdSentCommands') {
$attr{$name}{'cmdSentCommands'} = $aVal;
} elsif ($aName eq 'cmdFavorites') {
$attr{$name}{'cmdFavorites'} = $aVal;
} elsif ($aName eq 'cmdFavorites') {
$attr{$name}{'cmdFavorites'} = $aVal;
} elsif ($aName eq 'favorites') {
$attr{$name}{'favorites'} = $aVal;
} elsif ($aName eq 'favorites') {
$attr{$name}{'favorites'} = $aVal;
# Empty current alias list in hash
if ( defined( $hash->{AliasCmds} ) ) {
@ -574,9 +591,10 @@ sub TelegramBot_Attr(@) {
my @clist = split( /;/, $aVal);
foreach my $cs ( @clist ) {
if ( $cs =~ /^\s*((\/[^=]+)=)(.*)$/ ) {
my $alx = $2;
my $alcmd = $3;
my ( $alias, $desc, $parsecmd, $needsConfirm ) = TelegramBot_SplitFavoriteDef( $hash, $cs );
if ( $alias ) {
my $alx = $alias;
my $alcmd = $parsecmd;
Log3 $name, 2, "TelegramBot_Attr $name: Alias $alcmd defined multiple times" if ( defined( $hash->{AliasCmds}{$alx} ) );
$hash->{AliasCmds}{$alx} = $alcmd;
@ -587,7 +605,7 @@ sub TelegramBot_Attr(@) {
$aVal =~ s/^\s+|\s+$//g;
$attr{$name}{'cmdRestrictedPeer'} = $aVal;
} elsif ( ($aName eq 'defaultPeerCopy') ||
} elsif ( ($aName eq 'defaultPeerCopy') ||
($aName eq 'saveStateOnContactChange') ||
($aName eq 'cmdReturnEmptyResult') ||
($aName eq 'cmdTriggerOnly') ||
@ -620,16 +638,16 @@ sub TelegramBot_Attr(@) {
# wait some time before next polling is starting
TelegramBot_ResetPolling( $hash );
} elsif ($aName eq 'pollingVerbose') {
} elsif ($aName eq 'pollingVerbose') {
return "\"TelegramBot_Attr: \" Incorrect value given for pollingVerbose" if ( $aVal !~ /^((1_Digest)|(2_Log)|(0_None))$/ );
$attr{$name}{'pollingVerbose'} = $aVal;
}
$_[3] = $aVal;
}
}
return undef;
return undef;
}
@ -675,6 +693,39 @@ sub TelegramBot_checkCmdKeyword($$$$$) {
}
#####################################
#####################################
# INTERNAL: Split Favorite def in alias(optional), description (optional), parsecmd, needsConfirm
sub TelegramBot_SplitFavoriteDef($$) {
my ($hash, $cmd ) = @_;
my $name = $hash->{NAME};
# Valid favoritedef
# list TYPE=SOMFY
# ?set TYPE=CUL_WM getconfig
# /rolladen=list TYPE=SOMFY
# /rolladen=?list TYPE=SOMFY
# /[Liste Rolladen]=list TYPE=SOMFY
# /[Liste Rolladen]=?list TYPE=SOMFY
# /rolladen[Liste Rolladen]=list TYPE=SOMFY
# /rolladen[Liste Rolladen]=list TYPE=SOMFY
my ( $alias, $desc, $parsecmd, $confirm );
if ( $cmd =~ /^\s*((\/[^\[=]+)?(\[([^\]]+)\])?=)?(\??)(.*)$/ ) {
$alias = $2;
$desc = $4;
$confirm = $5;
$parsecmd = $6;
# Debug "Parse 1 a:".$alias.": d:".$desc.": c:".$parsecmd.":";
} else {
Log3 $name, 1, "TelegramBot_SplitFavoriteDef invalid favorite definition :$cmd: ";
}
return ( $alias, $desc, $parsecmd, (($confirm eq "?")?1:0) );
}
#####################################
#####################################
# INTERNAL: handle sentlast and favorites
@ -692,9 +743,9 @@ sub TelegramBot_SentFavorites($$$$) {
my @clist = split( /;/, $slc);
my $isConfirm;
if ( $cmd =~ /^\s*([0-9]+)(\??)[^0-9=]*=.*$/ ) {
if ( $cmd =~ /^\s*([0-9]+)(\??)\s*=.*$/ ) {
$cmd = $1;
$isConfirm = ($2 eq "?")?1:0;
$isConfirm = ($2 eq "?")?1:0;
}
# if given a number execute the numbered favorite as a command
@ -704,26 +755,29 @@ sub TelegramBot_SentFavorites($$$$) {
Log3 $name, 4, "TelegramBot_SentFavorites exec cmd :$cmdId: ";
if ( ( $cmdId >= 0 ) && ( $cmdId < scalar( @clist ) ) ) {
my $ecmd = $clist[$cmdId];
if ( $ecmd =~ /^\s*((\/[^=]+)=)(.*)$/ ) {
$ecmd = $3;
}
if ( ( ! $isConfirm ) && ( $ecmd =~ /^\s*\?(.*)$/ ) ) {
# ask first for confirmation
$ecmd = $1;
my ( $alias, $desc, $parsecmd, $needsConfirm ) = TelegramBot_SplitFavoriteDef( $hash, $ecmd );
$ecmd = $parsecmd;
# Debug "Needsconfirm: ". $needsConfirm;
if ( ( ! $isConfirm ) && ( $needsConfirm ) ) {
# ask first for confirmation
my $fcmd = AttrVal($name,'cmdFavorites',undef);
my @tmparr;
my @keys = ();
my @tmparr1 = ( $fcmd.$cmd."? = ".$ecmd );
my @tmparr1 = ( TelegramBot_PutToUTF8( $fcmd.$cmd."? = ".(($desc)?$desc:$parsecmd)." ausführen?" ) );
push( @keys, \@tmparr1 );
my @tmparr2 = ( "Abbruch" );
push( @keys, \@tmparr2 );
my $jsonkb = TelegramBot_MakeKeyboard( $hash, 1, @keys );
$ret = "TelegramBot fhem : ($mpeernorm)\n Bestätigung \n";
# LOCAL: External message
$ret = AttrVal( $name, 'textResponseConfirm', 'TelegramBot FHEM : $peer\n Bestätigung \n');
$ret =~ s/\$peer/$mpeernorm/g;
# $ret = "TelegramBot FHEM : ($mpeernorm)\n Bestätigung \n";
return TelegramBot_SendIt( $hash, $mpeernorm, $ret, $jsonkb, 0 );
@ -745,17 +799,21 @@ sub TelegramBot_SentFavorites($$$$) {
foreach my $cs ( @clist ) {
$cnt += 1;
my @tmparr = ( $fcmd.$cnt." = ".$cs );
my ( $alias, $desc, $parsecmd, $needsConfirm ) = TelegramBot_SplitFavoriteDef( $hash, $cs );
my @tmparr = ( TelegramBot_PutToUTF8( $fcmd.$cnt." = ".($alias?$alias." = ":"").(($desc)?$desc:$parsecmd) ) );
push( @keys, \@tmparr );
}
# my @tmparr = ( $fcmd."0 = Abbruch" );
# my @tmparr = ( TelegramBot_PutToUTF8( $fcmd."0 = Abbruch" ) );
# push( @keys, \@tmparr );
my $jsonkb = TelegramBot_MakeKeyboard( $hash, 1, @keys );
Log3 $name, 5, "TelegramBot_SentFavorites keyboard:".$jsonkb.": ";
$ret = "TelegramBot fhem : ($mpeernorm)\n Favorites \n";
# LOCAL: External message
$ret = AttrVal( $name, 'textResponseFavorites', 'TelegramBot FHEM : $peer\n Favoriten \n');
$ret =~ s/\$peer/$mpeernorm/g;
# $ret = "TelegramBot FHEM : ($mpeernorm)\n Favorites \n";
$ret = TelegramBot_SendIt( $hash, $mpeernorm, $ret, $jsonkb, 0 );
@ -800,7 +858,7 @@ sub TelegramBot_SentLastCommand($$$) {
my @keys = ();
foreach my $cs ( @cmds ) {
my @tmparr = ( $cs );
my @tmparr = ( TelegramBot_PutToUTF8( $cs ) );
push( @keys, \@tmparr );
}
# my @tmparr = ( $fcmd."0 = Abbruch" );
@ -808,7 +866,10 @@ sub TelegramBot_SentLastCommand($$$) {
my $jsonkb = TelegramBot_MakeKeyboard( $hash, 1, @keys );
$ret = "TelegramBot fhem : $mpeernorm \n Last Commands \n";
# LOCAL: External message
$ret = AttrVal( $name, 'textResponseCommands', 'TelegramBot FHEM : $peer\n Letzte Befehle \n');
$ret =~ s/\$peer/$mpeernorm/g;
# $ret = "TelegramBot FHEM : $mpeernorm \n Last Commands \n";
# overwrite ret with result from SendIt --> send response
$ret = TelegramBot_SendIt( $hash, $mpeernorm, $ret, $jsonkb, 0 );
@ -890,17 +951,36 @@ sub TelegramBot_ExecuteCommand($$$) {
$defpeer = AttrVal($name,'defaultPeer',undef) if ( ! defined( $defpeer ) );
$defpeer = undef if ( $defpeer eq $mpeernorm );
my $retstart = "TelegramBot fhem";
$retstart .= " from $pname ($mpeernorm)" if ( defined( $defpeer ) );
# LOCAL: External message
my $retMsg = AttrVal( $name, 'textResponseResult', 'TelegramBot FHEM : $peer\n Befehl:$cmd:\n Ergebnis:\n$result \n ');
$retMsg =~ s/\$cmd/$cmd/g;
my $retempty = AttrVal($name,'cmdReturnEmptyResult',1);
if ( defined( $defpeer ) ) {
$retMsg =~ s/\$peer/$pname/g;
} else {
$retMsg =~ s/\$peer//g;
}
if ( ( ! defined( $ret ) ) || ( length( $ret) == 0 ) ) {
$retMsg =~ s/\$result/OK/g;
$ret = $retMsg if ( AttrVal($name,'cmdReturnEmptyResult',1) );
} elsif ( ! $isMediaStream ) {
$retMsg =~ s/\$result/$ret/g;
$ret = $retMsg;
}
# my $retstart = "TelegramBot FHEM";
# $retstart .= " from $pname ($mpeernorm)" if ( defined( $defpeer ) );
# my $retempty = AttrVal($name,'cmdReturnEmptyResult',1);
# undef is considered ok
if ( ( ! defined( $ret ) ) || ( length( $ret) == 0 ) ) {
$ret = "$retstart cmd :$cmd: result OK" if ( $retempty );
} elsif ( ! $isMediaStream ) {
$ret = "$retstart cmd :$cmd: result :$ret:";
}
# if ( ( ! defined( $ret ) ) || ( length( $ret) == 0 ) ) {
# # : External message
# $ret = "$retstart cmd :$cmd: result OK" if ( $retempty );
# } elsif ( ! $isMediaStream ) {
# $ret = "$retstart cmd :$cmd: result :$ret:";
# }
Log3 $name, 5, "TelegramBot_ExecuteCommand $name: ".TelegramBot_MsgForLog($ret, $isMediaStream ).": ";
if ( ( defined( $ret ) ) && ( length( $ret) != 0 ) ) {
@ -937,7 +1017,7 @@ sub TelegramBot_ExecuteCommand($$$) {
# add a command to the StoredCommands reading
# hash, cmd
sub TelegramBot_AddStoredCommands($$) {
my ($hash, $cmd) = @_;
my ($hash, $cmd) = @_;
my $stcmds = ReadingsVal($hash->{NAME},"StoredCommands","");
$stcmds = $stcmds;
@ -965,7 +1045,7 @@ sub TelegramBot_AddStoredCommands($$) {
# Always executes and returns on first match also in case of error
sub Telegram_HandleCommandInMessages($$$$)
{
my ( $hash, $mpeernorm, $mtext, $mid ) = @_;
my ( $hash, $mpeernorm, $mtext, $mid ) = @_;
my $name = $hash->{NAME};
my $cmdRet;
@ -1036,7 +1116,7 @@ sub Telegram_HandleCommandInMessages($$$$)
# > returns string in case of error or the content of the result object if ok
sub TelegramBot_DoUrlCommand($$)
{
my ( $hash, $url ) = @_;
my ( $hash, $url ) = @_;
my $name = $hash->{NAME};
my $ret;
@ -1093,9 +1173,9 @@ sub TelegramBot_DoUrlCommand($$)
# addPar is caption for images / keyboard for text
sub TelegramBot_SendIt($$$$$;$)
{
my ( $hash, @args) = @_;
my ( $hash, @args) = @_;
my ( $peers, $msg, $addPar, $isMedia, $retryCount) = @args;
my ( $peers, $msg, $addPar, $isMedia, $retryCount) = @args;
my $name = $hash->{NAME};
if ( ! defined( $retryCount ) ) {
@ -1104,7 +1184,7 @@ sub TelegramBot_SendIt($$$$$;$)
# increase retrycount for next try
$args[4] = $retryCount+1;
Log3 $name, 5, "TelegramBot_SendIt $name: called ";
# ensure sentQueue exists
@ -1166,7 +1246,7 @@ sub TelegramBot_SendIt($$$$$;$)
if ( ! $isMedia ) {
$TelegramBot_hu_do_params{url} = $hash->{URL}."sendMessage";
# $TelegramBot_hu_do_params{url} = "http://requestb.in/sgqxy2sg";
# $TelegramBot_hu_do_params{url} = "http://requestb.in";
if ( length($msg) > 1000 ) {
$hash->{sentMsgText} = substr($msg,0, 1000)."...";
@ -1189,7 +1269,6 @@ sub TelegramBot_SendIt($$$$$;$)
(( defined( $addPar ) )?" - ".$addPar:"");
$TelegramBot_hu_do_params{url} = $hash->{URL}."sendPhoto";
# $TelegramBot_hu_do_params{url} = "http://requestb.in/1g8u9na1";
# add caption
if ( defined( $addPar ) ) {
@ -1253,7 +1332,7 @@ sub TelegramBot_SendIt($$$$$;$)
# > returns string in case of error or undef
sub TelegramBot_AddMultipart($$$$$$)
{
my ( $hash, $params, $parname, $parheader, $parcontent, $isMedia ) = @_;
my ( $hash, $params, $parname, $parheader, $parcontent, $isMedia ) = @_;
my $name = $hash->{NAME};
my $ret;
@ -1319,7 +1398,7 @@ sub TelegramBot_AddMultipart($$$$$$)
# > returns string in case of error or undef
sub TelegramBot_MakeKeyboard($$@)
{
my ( $hash, $onetime_hide, @keys ) = @_;
my ( $hash, $onetime_hide, @keys ) = @_;
my $name = $hash->{NAME};
my $ret;
@ -1351,7 +1430,7 @@ sub TelegramBot_UpdatePoll($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "TelegramBot_UpdatePoll $name: called ";
if ( $hash->{POLLING} ) {
@ -1373,7 +1452,7 @@ sub TelegramBot_UpdatePoll($)
$hash->{FAILS} = 0;
my $wait = $hash->{OLDFAILS}+2;
Log3 $name, 5, "TelegramBot_UpdatePoll $name: got fails :".$hash->{OLDFAILS}.": wait ".$wait." seconds";
InternalTimer(gettimeofday()+$wait, "TelegramBot_UpdatePoll", $hash,0);
InternalTimer(gettimeofday()+$wait, "TelegramBot_UpdatePoll", $hash,0);
return;
} elsif ( defined($hash->{OLDFAILS}) ) {
# oldfails defined means
@ -1595,8 +1674,8 @@ sub TelegramBot_Callback($$$)
# Also set sentMsg Id and result in Readings
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "sentMsgResult", $ret);
readingsBulkUpdate($hash, "sentMsgId", ((defined($msgId))?$msgId:"") );
readingsBulkUpdate($hash, "sentMsgResult", $ret);
readingsBulkUpdate($hash, "sentMsgId", ((defined($msgId))?$msgId:"") );
readingsEndUpdate($hash, 1);
if ( scalar( @{ $hash->{sentQueue} } ) ) {
@ -1608,16 +1687,6 @@ sub TelegramBot_Callback($$$)
}
#####################################
# INTERNAL: Convert (Mark) a scalar as UTF8
sub TelegramBot_GetUTF8Back( $ ) {
my ( $data ) = @_;
return encode('utf8', $data);
}
#####################################
# INTERNAL: _ParseMsg handle a message from the update call
# params are the hash, the updateid and the actual message
@ -1752,24 +1821,24 @@ sub TelegramBot_ParseMsg($$$)
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "prevMsgId", $hash->{READINGS}{msgId}{VAL});
readingsBulkUpdate($hash, "prevMsgPeer", $hash->{READINGS}{msgPeer}{VAL});
readingsBulkUpdate($hash, "prevMsgPeerId", $hash->{READINGS}{msgPeerId}{VAL});
readingsBulkUpdate($hash, "prevMsgChat", $hash->{READINGS}{msgChat}{VAL});
readingsBulkUpdate($hash, "prevMsgText", $hash->{READINGS}{msgText}{VAL});
readingsBulkUpdate($hash, "prevMsgFileId", $hash->{READINGS}{msgFileId}{VAL});
readingsBulkUpdate($hash, "prevMsgId", $hash->{READINGS}{msgId}{VAL});
readingsBulkUpdate($hash, "prevMsgPeer", $hash->{READINGS}{msgPeer}{VAL});
readingsBulkUpdate($hash, "prevMsgPeerId", $hash->{READINGS}{msgPeerId}{VAL});
readingsBulkUpdate($hash, "prevMsgChat", $hash->{READINGS}{msgChat}{VAL});
readingsBulkUpdate($hash, "prevMsgText", $hash->{READINGS}{msgText}{VAL});
readingsBulkUpdate($hash, "prevMsgFileId", $hash->{READINGS}{msgFileId}{VAL});
readingsEndUpdate($hash, 0);
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "msgId", $mid);
readingsBulkUpdate($hash, "msgPeer", TelegramBot_GetFullnameForContact( $hash, $mpeernorm ));
readingsBulkUpdate($hash, "msgChat", TelegramBot_GetFullnameForChat( $hash, $chatId ) );
readingsBulkUpdate($hash, "msgPeerId", $mpeernorm);
readingsBulkUpdate($hash, "msgId", $mid);
readingsBulkUpdate($hash, "msgPeer", TelegramBot_GetFullnameForContact( $hash, $mpeernorm ));
readingsBulkUpdate($hash, "msgChat", TelegramBot_GetFullnameForChat( $hash, $chatId ) );
readingsBulkUpdate($hash, "msgPeerId", $mpeernorm);
readingsBulkUpdate($hash, "msgText", $mtext);
readingsBulkUpdate($hash, "msgFileId", ( ( defined( $mfileid ) ) ? $mfileid : "" ) );
readingsBulkUpdate($hash, "msgFileId", ( ( defined( $mfileid ) ) ? $mfileid : "" ) );
readingsEndUpdate($hash, 1);
@ -2104,6 +2173,14 @@ sub TelegramBot_ContactUpdate($@) {
foreach my $user ( @contacts ) {
my $contactString = TelegramBot_userObjectToString( $user );
# keep the username part of the new contatc for deleting old users with same username
my $unamepart;
my @clist = split( /:/, $contactString );
if (defined($clist[2])) {
$unamepart = $clist[2];
}
if ( ! defined( $hash->{Contacts}{$user->{id}} ) ) {
Log3 $hash->{NAME}, 3, "TelegramBot_ContactUpdate new contact :".$contactString.":";
next if ( AttrVal($hash->{NAME},'allowUnknownContacts',1) == 0 );
@ -2111,6 +2188,18 @@ sub TelegramBot_ContactUpdate($@) {
} elsif ( $contactString ne $hash->{Contacts}{$user->{id}} ) {
Log3 $hash->{NAME}, 3, "TelegramBot_ContactUpdate updated contact :".$contactString.":";
}
# remove all contacts with same username
if ( defined( $unamepart ) ) {
my $dupid = TelegramBot_GetIdForPeer( $hash, $unamepart );
while ( $dupid ) {
Log3 $hash->{NAME}, 3, "TelegramBot_ContactUpdate removed stale/duplicate contact ($dupid:$unamepart):".$hash->{Contacts}{$dupid}.":" if ( $dupid ne $user->{id} );
delete( $hash->{Contacts}{$dupid} );
$dupid = TelegramBot_GetIdForPeer( $hash, $unamepart );
}
}
# set new contact data
$hash->{Contacts}{$user->{id}} = $contactString;
}
@ -2135,19 +2224,19 @@ sub TelegramBot_ContactUpdate($@) {
Log3 $hash->{NAME}, 2, "TelegramBot_ContactUpdate Updated Contact list :".$rc.":";
}
return;
return;
}
#####################################
# INTERNAL: Convert TelegramBot user and chat object to string
sub TelegramBot_userObjectToString($) {
my ( $user ) = @_;
my ( $user ) = @_;
my $ret = $user->{id}.":";
# user objects do not contain a type field / chat objects need to contain a type but only if type=group it is really a group
if ( ( defined( $user->{type} ) ) && ( $user->{type} eq "group" ) ) {
# user objects do not contain a type field / chat objects need to contain a type but only if type=group or type=supergroup it is really a group
if ( ( defined( $user->{type} ) ) && ( ( $user->{type} eq "group" ) || ( $user->{type} eq "supergroup" ) ) ) {
$ret .= ":";
@ -2155,7 +2244,9 @@ sub TelegramBot_userObjectToString($) {
} else {
my $part = $user->{first_name};
my $part = "";
$part .= $user->{first_name} if ( defined( $user->{first_name} ) );
$part .= " ".$user->{last_name} if ( defined( $user->{last_name} ) );
$ret .= TelegramBot_encodeContactString($part).":";
@ -2169,7 +2260,7 @@ sub TelegramBot_userObjectToString($) {
#####################################
# INTERNAL: Convert TelegramBot user and chat object to string
sub TelegramBot_encodeContactString($) {
my ($str) = @_;
my ($str) = @_;
$str =~ s/:/_/g;
$str =~ s/^\s+|\s+$//g;
@ -2203,7 +2294,11 @@ sub TelegramBot_checkAllowedPeer($$$) {
# unauthorized fhem cmd
Log3 $name, 1, "TelegramBot unauthorized cmd from user :$pname: ($mpeer) \n Msg: $msg";
my $ret = "UNAUTHORIZED: TelegramBot fhem request from user :$pname: ($mpeer) \n Msg: $msg";
# LOCAL: External message
my $ret = AttrVal( $name, 'textResponseUnauthorized', 'UNAUTHORIZED: TelegramBot FHEM request from user :$peer \n Msg: $msg');
$ret =~ s/\$peer/$pname ($mpeer)/g;
$ret =~ s/\$msg/$msg/g;
# my $ret = "UNAUTHORIZED: TelegramBot FHEM request from user :$pname: ($mpeer) \n Msg: $msg";
# send unauthorized to defaultpeer
my $defpeer = AttrVal($name,'defaultPeer',undef);
@ -2225,6 +2320,26 @@ sub TelegramBot_checkAllowedPeer($$$) {
##############################################################################
#####################################
# INTERNAL: Convert (Mark) a scalar as UTF8 - coming from telegram
sub TelegramBot_GetUTF8Back( $ ) {
my ( $data ) = @_;
return encode('utf8', $data);
}
#####################################
# INTERNAL: used to encode a string aas Utf-8 coming from the code
sub TelegramBot_PutToUTF8( $ ) {
my ( $data ) = @_;
return decode('utf8', $data);
}
######################################
# Get a string and identify possible media streams
# PNG is tested
@ -2235,17 +2350,17 @@ sub TelegramBot_checkAllowedPeer($$$) {
# and extension without dot as 2nd list element
sub TelegramBot_IdentifyStream($$) {
my ($hash, $msg) = @_;
my ($hash, $msg) = @_;
# signatures for media files are documented here --> https://en.wikipedia.org/wiki/List_of_file_signatures
# seems sometimes more correct: https://wangrui.wordpress.com/2007/06/19/file-signatures-table/
return (-1,"png") if ( $msg =~ /^\x89PNG\r\n\x1a\n/ ); # PNG
return (-1,"jpg") if ( $msg =~ /^\xFF\xD8\xFF/ ); # JPG not necessarily complete, but should be fine here
return (-2 ,"mp3") if ( $msg =~ /^\xFF\xF3/ ); # MP3 MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag
return (-2 ,"mp3") if ( $msg =~ /^\xFF\xFB/ ); # MP3 MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag
return (-2 ,"mp3") if ( $msg =~ /^\xFF\xF3/ ); # MP3 MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag
return (-2 ,"mp3") if ( $msg =~ /^\xFF\xFB/ ); # MP3 MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag
# MP3 MPEG-1 Layer 3 file with an ID3v2 tag
# MP3 MPEG-1 Layer 3 file with an ID3v2 tag
# starts with ID3 then version (most popular 03, new 04 seldom used, old 01 and 02) ==> Only 2,3 and 4 are tested currently
return (-2 ,"mp3") if ( $msg =~ /^ID3\x03/ );
return (-2 ,"mp3") if ( $msg =~ /^ID3\x04/ );
@ -2264,7 +2379,7 @@ sub TelegramBot_IdentifyStream($$) {
#####################################
# INTERNAL: prepare msg/ret for log file
sub TelegramBot_MsgForLog($;$) {
my ($msg, $stream) = @_;
my ($msg, $stream) = @_;
if ( ! defined( $msg ) ) {
return "<undef>";
@ -2278,12 +2393,12 @@ sub TelegramBot_MsgForLog($;$) {
# read binary file for Phototransfer - returns undef or empty string on error
#
sub TelegramBot_BinaryFileRead($$) {
my ($hash, $fileName) = @_;
my ($hash, $fileName) = @_;
return '' if ( ! (-e $fileName) );
my $fileData = '';
open TGB_BINFILE, '<'.$fileName;
binmode TGB_BINFILE;
while (<TGB_BINFILE>){
@ -2300,7 +2415,7 @@ sub TelegramBot_BinaryFileRead($$) {
# write binary file for (hest hash, filename and the data
#
sub TelegramBot_BinaryFileWrite($$$) {
my ($hash, $fileName, $data) = @_;
my ($hash, $fileName, $data) = @_;
open TGB_BINFILE, '>'.$fileName;
binmode TGB_BINFILE;
@ -2462,21 +2577,23 @@ sub TelegramBot_BinaryFileWrite($$$) {
</li>
<li><code>favorites &lt;list of commands&gt;</code><br>Specify a list of favorite commands for Fhem (without cmdKeyword). Multiple commands are separated by semicolon (;). This also means that only simple commands (without embedded semicolon) can be defined. <br>
<br>
Favorite commands are fhem commands with an optional alias for the command given. The alias can be sent as message (instead of the favoriteCmd) to execute the command. Favorites are specified as prefix to the command starting with a '/' and separated from the command with a '='.
Favorite commands are fhem commands with an optional alias for the command given. The alias can be sent as message (instead of the favoriteCmd) to execute the command. Before the favorite command also an alias (other shortcut for the favorite) or/and a descriptive text (enclosed in []) can be specifed. If alias or description is specified this needs to be prefixed with a '/' and the alias if given needs to be specified first.
<br>
Example: Assuming cmdFavorites is set to a value of <code>favorite</code> and this attribute is set to a value of
<br><code>get lights status; /light=set lights on; /dark=set lights off; /heating=set heater;</code> <br>
<br><code>get lights status; /light=set lights on; /dark[Make it dark]=set lights off; /heating=set heater; /[status]=get heater status;</code> <br>
<ul>
<li>Then a message "favorite0" to the bot would execute the command <code>get lights status</code></li>
<li>A message "favorite 1" or "/light" to the bot would execute the command <code>set lights on</code></li>
<li>A message "/heating on" to the bot would execute the command <code>set heater on</code><br> (Attention the remainder after the alias will be added to the command in fhem!)</li>
<li>Then a message "favorite1" to the bot would execute the command <code>get lights status</code></li>
<li>A message "favorite 2" or "/light" to the bot would execute the command <code>set lights on</code>. And the favorite would show as "make it dark" in the list of favorites.</li>
<li>A message "/heating on" or "favorite 3 on" to the bot would execute the command <code>set heater on</code><br> (Attention the remainder after the alias will be added to the command in fhem!)</li>
<li>A message "favorite 4" to the bot would execute the command <code>get heater status</code> and this favorite would show as "status" as a description in the favorite list</li>
</ul>
<br>
Favorite commands can also be prefixed with a question mark (?) to enable a confirmation being requested before executing the command.
Favorite commands can also be prefixed with a question mark ('?') to enable a confirmation being requested before executing the command.
<br>
Examples: <code>get lights status; /light=?set lights on; /dark=set lights off; ?set heater;</code> <br>
<br>
Meaning the full format for a single favorite is <code>/alias[description]=command</code> where the alias can be empty or <code>/alias=command</code> or just the <code>command</code>. In any case the command can be also prefixed with a '?'.
</li>
@ -2504,7 +2621,7 @@ sub TelegramBot_BinaryFileWrite($$$) {
</li>
<li><code>maxReturnSize &lt;number of chars&gt;</code><br>Maximum size of command result returned as a text message including header (Default is unlimited). The internal shown on the device is limited to 1000 chars.
</li>
<li><code>maxRetries &lt;0,1,2,3,4,5&gt;</code><br>MSpecify the number of retries for sending a message in case of a failure. The first retry is sent after 10sec, the second after 100, then after 1000s (~16min), then after 10000s (~2.5h), then after ~ a day. Setinng the value to 0 (default) will result in no retries.
<li><code>maxRetries &lt;0,1,2,3,4,5&gt;</code><br>Specify the number of retries for sending a message in case of a failure. The first retry is sent after 10sec, the second after 100, then after 1000s (~16min), then after 10000s (~2.5h), then after approximately a day. Setting the value to 0 (default) will result in no retries.
</li>
<br><br>
@ -2514,7 +2631,18 @@ sub TelegramBot_BinaryFileWrite($$$) {
<li><code>allowUnknownContacts &lt;1 or 0&gt;</code><br>Allow new contacts to be added automatically (1 - Default) or restrict message reception only to known contacts and unknwown contacts will be ignored (0).
</li>
<li><a href="#verbose">verbose</a></li>
<br><br>
<li><code>textResponseConfirm &lt;TelegramBot FHEM : $peer\n Bestätigung \n&gt;</code><br>Text to be sent when a confirmation for a command is requested. Default is shown here and $peer will be replaced with the actual contact full name if added.
</li>
<li><code>textResponseFavorites &lt;TelegramBot FHEM : $peer\n Favoriten \n&gt;</code><br>Text to be sent as starter for the list of favorites. Default is shown here and $peer will be replaced with the actual contact full name if added.
</li>
<li><code>textResponseCommands &lt;TelegramBot FHEM : $peer\n Letzte Befehle \n&gt;</code><br>Text to be sent as starter for the list of last commands. Default is shown here and $peer will be replaced with the actual contact full name if added.
</li>
<li><code>textResponseResult &lt;TelegramBot FHEM : $peer\n Befehl:$cmd:\n Ergebnis:\n$result\n&gt;</code><br>Text to be sent as result for a cmd execution. Default is shown here and $peer will be replaced with the actual contact full name if added. Similarly $cmd and $result will be replaced with the cmd and the execution result.
</li>
<li><code>textResponseUnauthorized &lt;UNAUTHORIZED: TelegramBot FHEM request from user :$peer\n Msg: $msg&gt;</code><br>Text to be sent as warning for unauthorized command requests. Default is shown here and $peer will be replaced with the actual contact full name and id if added. $msg will be replaced with the sent message.
</li>
</ul>
<br><br>