2019-10-28 19:26:45 +01:00
< ? php
/* Copyright 2016 - 2019 Daniil Gentili
* ( https :// daniil . it )
* This file is part of MadelineProto .
* MadelineProto is free software : you can redistribute it and / or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation , either version 3 of the License , or ( at your option ) any later version .
* MadelineProto is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU Affero General Public License for more details .
* You should have received a copy of the GNU General Public License along with MadelineProto .
* If not , see < http :// www . gnu . org / licenses />.
*/
2023-08-14 20:54:47 +02:00
declare ( strict_types = 1 );
2020-11-27 17:08:29 +01:00
use danog\Loop\ResumableSignalLoop ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\API ;
2020-11-30 16:21:01 +01:00
use danog\MadelineProto\EventHandler ;
2023-08-15 10:15:20 +02:00
use danog\MadelineProto\Broadcast\Progress ;
use danog\MadelineProto\Broadcast\Status ;
use danog\MadelineProto\EventHandler\Attributes\Cron ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\EventHandler\Attributes\Handler ;
2023-08-15 10:15:20 +02:00
use danog\MadelineProto\EventHandler\Filter\FilterCommand ;
use danog\MadelineProto\EventHandler\Filter\FilterRegex ;
use danog\MadelineProto\EventHandler\Filter\FilterText ;
use danog\MadelineProto\EventHandler\Filter\FilterTextCaseInsensitive ;
use danog\MadelineProto\EventHandler\Message ;
use danog\MadelineProto\EventHandler\Message\Service\DialogPhotoChanged ;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming ;
2023-08-15 10:15:20 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\IsReply ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\Exception ;
use danog\MadelineProto\LocalFile ;
use danog\MadelineProto\RPCErrorException ;
use danog\MadelineProto\SimpleEventHandler ;
use danog\MadelineProto\Tools ;
use danog\MadelineProto\VoIP ;
use danog\MadelineProto\VoIP\CallState ;
2020-11-27 17:08:29 +01:00
2023-08-15 11:32:47 +02:00
if ( file_exists ( 'vendor/autoload.php' )) {
require 'vendor/autoload.php' ;
} else {
2023-08-14 20:54:47 +02:00
if ( ! file_exists ( 'madeline.php' )) {
copy ( 'https://phar.madelineproto.xyz/madeline.php' , 'madeline.php' );
2019-10-28 19:26:45 +01:00
}
include 'madeline.php' ;
2023-08-15 11:32:47 +02:00
}
2019-10-28 19:26:45 +01:00
2023-08-15 12:18:37 +02:00
$songs = glob ( '*ogg' );
2023-08-15 12:16:24 +02:00
if ( ! $songs ) {
2023-08-15 12:18:37 +02:00
die ( 'No songs defined! Convert some songs by sending them to https://t.me/libtgvoipbot and putting them in the current directory' . PHP_EOL );
2023-08-15 12:16:24 +02:00
}
2023-08-14 20:54:47 +02:00
/* class MessageLoop extends ResumableSignalLoop
2019-10-28 19:26:45 +01:00
{
2020-11-27 17:08:29 +01:00
const INTERVAL = 10000 ;
2019-10-28 19:26:45 +01:00
private $timeout ;
private $call ;
2020-11-30 16:21:01 +01:00
private EventHandler $API ;
2019-10-28 19:26:45 +01:00
public function __construct ( $API , $call , $timeout = self :: INTERVAL )
{
$this -> API = $API ;
$this -> call = $call ;
$this -> timeout = $timeout ;
}
2023-08-14 20:54:47 +02:00
public function loop () : void
2019-10-28 19:26:45 +01:00
{
$MadelineProto = $this -> API ;
2020-11-30 16:21:01 +01:00
$logger = $MadelineProto -> getLogger ();
2019-10-28 19:26:45 +01:00
while ( true ) {
2020-03-01 16:30:44 +01:00
do {
2023-08-14 20:54:47 +02:00
$result = $this -> waitSignal ( $this -> pause ( $this -> timeout ));
2019-10-28 19:26:45 +01:00
if ( $result ) {
$logger -> logger ( " Got signal in $this , exiting " );
return ;
}
2020-03-01 16:30:44 +01:00
} while ( ! isset ( $this -> call -> mId ));
2023-08-14 20:54:47 +02:00
$result = $this -> waitSignal ( $this -> pause ( $this -> timeout ));
2019-10-28 19:26:45 +01:00
try {
2023-08-14 20:54:47 +02:00
$message = 'Total running calls: ' . count ( $MadelineProto -> getEventHandler () -> calls ) . PHP_EOL . PHP_EOL . $this -> call -> getDebugString ();
2020-03-01 16:30:44 +01:00
$message .= PHP_EOL . PHP_EOL . PHP_EOL ;
2023-08-14 20:54:47 +02:00
$message .= " Emojis: " . implode ( '' , $this -> call -> getVisualization ());
2020-03-02 15:52:23 +01:00
2023-08-14 20:54:47 +02:00
$MadelineProto -> messages -> editMessage ([ 'id' => $this -> call -> mId , 'peer' => $this -> call -> otherID , 'message' => $message ]);
} catch ( RPCErrorException $e ) {
2019-10-28 19:26:45 +01:00
$MadelineProto -> logger ( $e );
}
}
}
public function __toString () : string
{
2023-08-14 20:54:47 +02:00
return " VoIP message loop " . $this -> call -> otherID ;
2019-10-28 19:26:45 +01:00
}
}
class StatusLoop extends ResumableSignalLoop
{
2020-11-27 17:08:29 +01:00
const INTERVAL = 2000 ;
2019-10-28 19:26:45 +01:00
private $timeout ;
private $call ;
2020-11-30 16:21:01 +01:00
private EventHandler $API ;
2019-10-28 19:26:45 +01:00
public function __construct ( $API , $call , $timeout = self :: INTERVAL )
{
$this -> API = $API ;
$this -> call = $call ;
$this -> timeout = $timeout ;
}
2023-08-14 20:54:47 +02:00
public function loop () : void
2019-10-28 19:26:45 +01:00
{
$MadelineProto = $this -> API ;
2020-11-30 16:21:01 +01:00
$logger = $MadelineProto -> getLogger ();
2019-10-28 19:26:45 +01:00
$call = $this -> call ;
while ( true ) {
2023-08-14 20:54:47 +02:00
$result = $this -> waitSignal ( $this -> pause ( $this -> timeout ));
2019-10-28 19:26:45 +01:00
if ( $result ) {
$logger -> logger ( " Got signal in $this , exiting " );
2023-08-14 20:54:47 +02:00
$MadelineProto -> getEventHandler () -> cleanUpCall ( $call -> otherID );
2019-10-28 19:26:45 +01:00
return ;
}
2023-08-14 20:54:47 +02:00
if ( $call -> getCallState () === CallState :: ENDED ) {
$MadelineProto -> getEventHandler () -> cleanUpCall ( $call -> otherID );
if ( file_exists ( '/tmp/logs' . $call -> getCallID ()[ 'id' ] . '.log' )) {
@ unlink ( '/tmp/logs' . $call -> getCallID ()[ 'id' ] . '.log' );
2020-11-30 16:21:01 +01:00
try {
2023-08-14 20:54:47 +02:00
$me = $this -> API -> getEventHandler () -> getMe ();
$MadelineProto -> messages -> sendMedia ([
2019-10-28 19:26:45 +01:00
'reply_to_msg_id' => $this -> call -> mId ,
2023-08-14 20:54:47 +02:00
'peer' => $call -> otherID , 'message' => " Debug info by $me " ,
2019-10-28 19:26:45 +01:00
'media' => [
'_' => 'inputMediaUploadedDocument' ,
'file' => '/tmp/logs' . $call -> getCallID ()[ 'id' ] . '.log' ,
'attributes' => [
[ '_' => 'documentAttributeFilename' , 'file_name' => 'logs' . $call -> getCallID ()[ 'id' ] . '.log' ],
],
],
]);
2023-08-14 20:54:47 +02:00
} catch ( Exception $e ) {
2020-11-30 16:21:01 +01:00
$MadelineProto -> logger ( $e );
2023-08-14 20:54:47 +02:00
} catch ( RPCErrorException $e ) {
2020-11-30 16:21:01 +01:00
$MadelineProto -> logger ( $e );
2023-08-14 20:54:47 +02:00
} catch ( Exception $e ) {
2020-11-30 16:21:01 +01:00
$MadelineProto -> logger ( $e );
}
}
2023-08-14 20:54:47 +02:00
if ( file_exists ( '/tmp/stats' . $call -> getCallID ()[ 'id' ] . '.txt' )) {
@ unlink ( '/tmp/stats' . $call -> getCallID ()[ 'id' ] . '.txt' );
2019-10-28 19:26:45 +01:00
}
return ;
}
}
}
public function __toString () : string
{
2023-08-14 20:54:47 +02:00
return " VoIP status loop " . $this -> call -> otherID ;
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
} */
2019-10-28 19:26:45 +01:00
2023-08-14 20:54:47 +02:00
class MyEventHandler extends SimpleEventHandler
2019-10-28 19:26:45 +01:00
{
const ADMINS = [ 101374607 ]; // @danogentili, creator of MadelineProto
private $messageLoops = [];
private $statusLoops = [];
private $programmed_call ;
private $my_users ;
2020-03-01 16:43:46 +01:00
private $me ;
2019-10-28 19:26:45 +01:00
public $calls = [];
2023-08-14 20:54:47 +02:00
public function onStart () : void
2020-03-01 16:43:46 +01:00
{
2023-08-14 20:54:47 +02:00
$this -> me = '@' . ((( $this -> getSelf ())[ 'username' ]) ? ? 'magnaluna' );
$this -> programmed_call = [];
foreach ( $this -> programmed_call as $key => [ $user , $time ]) {
continue ;
$sleepTime = $time <= time () ? 0 : $time - time ();
Tools :: callFork ( function () use ( $sleepTime , $key , $user ) : void {
Tools :: sleep ( $sleepTime );
$this -> makeCall ( $user );
unset ( $this -> programmed_call [ $key ]);
});
}
2020-03-01 16:43:46 +01:00
}
2023-08-15 10:15:20 +02:00
private int $lastLog = 0 ;
/**
* Handles updates to an in - progress broadcast .
*/
public function onUpdateBroadcastProgress ( Progress $progress ) : void
{
if ( time () - $this -> lastLog > 5 || $progress -> status === Status :: FINISHED ) {
$this -> lastLog = time ();
$this -> sendMessageToAdmins (( string ) $progress );
}
}
#[FilterCommand('broadcast')]
public function broadcastCommand ( Message & FromAdmin $message ) : void
{
// We can broadcast messages to all users with /broadcast
if ( ! $message -> replyToMsgId ) {
$message -> reply ( " You should reply to the message you want to broadcast. " );
return ;
}
$this -> broadcastForwardMessages (
from_peer : $message -> senderId ,
message_ids : [ $message -> replyToMsgId ],
drop_author : true ,
pin : true ,
);
}
2020-03-01 19:08:58 +01:00
public function getMe () : string
{
return $this -> me ;
}
2020-02-29 19:02:51 +01:00
public function getReportPeers () : array
{
return self :: ADMINS ;
}
2023-08-14 20:54:47 +02:00
public function configureCall ( VoIP $call ) : void
2019-10-28 19:26:45 +01:00
{
2023-08-14 20:54:47 +02:00
$songs = glob ( '*ogg' );
if ( ! $songs ) {
2023-08-15 12:18:37 +02:00
throw new \AssertionError ( 'No songs defined! Convert some songs by sending them to https://t.me/libtgvoipbot and putting them in the current directory' );
2023-08-14 20:54:47 +02:00
}
$songs_length = count ( $songs );
2020-03-01 16:30:44 +01:00
2023-08-14 20:54:47 +02:00
for ( $x = 0 ; $x < $songs_length ; $x ++ ) {
shuffle ( $songs );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
foreach ( $songs as & $song ) {
$song = new LocalFile ( $song );
}
$call -> playOnHold ( ... $songs );
if ( $call -> getCallState () !== CallState :: ENDED ) {
$this -> calls [ $call -> otherID ] = $call ;
/* try {
$message = 'Total running calls: ' . count ( $this -> calls ) . PHP_EOL . PHP_EOL . $call -> getDebugString ();
2020-03-01 16:30:44 +01:00
//$message .= PHP_EOL;
//$message .= "Emojis: ".implode('', $call->getVisualization());
2023-08-14 20:54:47 +02:00
$call -> mId = $this -> messages -> sendMessage ([ 'peer' => $call -> otherID , 'message' => $message ])[ 'id' ];
} catch ( Throwable $e ) {
2019-10-28 19:26:45 +01:00
$this -> logger ( $e );
}
2023-08-14 20:54:47 +02:00
$this -> messageLoops [ $call -> otherID ] = new MessageLoop ( $this , $call );
$this -> statusLoops [ $call -> otherID ] = new StatusLoop ( $this , $call );
$this -> messageLoops [ $call -> otherID ] -> start ();
$this -> statusLoops [ $call -> otherID ] -> start (); */
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
//$this->messages->sendMessage(['message' => var_export($call->configuration, true), 'peer' => $call->otherID]);
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
public function cleanUpCall ( $user ) : void
2019-10-28 19:26:45 +01:00
{
if ( isset ( $this -> calls [ $user ])) {
unset ( $this -> calls [ $user ]);
}
if ( isset ( $this -> messageLoops [ $user ])) {
$this -> messageLoops [ $user ] -> signal ( true );
unset ( $this -> messageLoops [ $user ]);
}
if ( isset ( $this -> statusLoops [ $user ])) {
$this -> statusLoops [ $user ] -> signal ( true );
unset ( $this -> statusLoops [ $user ]);
}
}
2023-08-14 20:54:47 +02:00
public function makeCall ( $user ) : void
2019-10-28 19:26:45 +01:00
{
try {
if ( isset ( $this -> calls [ $user ])) {
2023-08-14 20:54:47 +02:00
if ( $this -> calls [ $user ] -> getCallState () === CallState :: ENDED ) {
$this -> cleanUpCall ( $user );
2019-10-28 19:26:45 +01:00
} else {
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'peer' => $user , 'message' => " I'm already in a call with you! " ]);
2019-10-28 19:26:45 +01:00
return ;
}
}
2023-08-14 20:54:47 +02:00
$this -> configureCall ( $this -> requestCall ( $user ));
} catch ( RPCErrorException $e ) {
2019-10-28 19:26:45 +01:00
try {
2023-08-15 10:08:47 +02:00
if ( $e -> rpc === " CALL_PROTOCOL_COMPAT_LAYER_INVALID " ) {
$e = " Please call me using Telegram Desktop, Telegram for Mac or Telegram Android! " ;
}
2019-10-28 19:26:45 +01:00
if ( $e -> rpc === 'USER_PRIVACY_RESTRICTED' ) {
2023-08-15 10:08:47 +02:00
$e = 'Please disable call privacy settings to make me call you (or call me yourself!)' ;
}
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'peer' => $user , 'message' => ( string ) $e ]);
} catch ( RPCErrorException $e ) {
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
} catch ( Throwable $e ) {
$this -> messages -> sendMessage ([ 'peer' => $user , 'message' => ( string ) $e ]);
2019-10-28 19:26:45 +01:00
}
}
2023-08-14 20:54:47 +02:00
public function handleMessage ( $chat_id , $from_id , $message ) : void
2019-10-28 19:26:45 +01:00
{
try {
if ( ! isset ( $this -> my_users [ $from_id ]) || $message === '/start' ) {
$this -> my_users [ $from_id ] = true ;
$message = '/call' ;
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'no_webpage' => true , 'peer' => $chat_id , 'message' => " Hi, I'm { $this -> me } the webradio.
2019-10-28 19:26:45 +01:00
Call _me_ to listen to some ** awesome ** music , or send / call to make _me_ call _you_ ( don ' t forget to disable call privacy settings ! ) .
You can also program a phone call with / program :
/ program 29 August 2018 - call me the 29 th of august 2018
/ program + 1 hour 30 minutes - call me in one hour and thirty minutes
/ program next Thursday - call me next Thursday at midnight
Send / start to see this message again .
I also provide advanced stats during calls !
I ' m a userbot powered by @ MadelineProto , created by @ danogentili .
Source code : https :// github . com / danog / MadelineProto
2023-08-15 10:15:20 +02:00
Propic art by magnaluna on [ deviantart ]( https :// magnaluna . deviantart . com ) .
Note for iOS users : the official Telegram iOS app has a bug which prevents me from working properly , I ' m looking into it , try calling from your Mac / Android / PC , instead !
" , 'parse_mode' => 'Markdown']);
2019-10-28 19:26:45 +01:00
}
if ( ! isset ( $this -> calls [ $from_id ]) && $message === '/call' ) {
2023-08-14 20:54:47 +02:00
$this -> makeCall ( $from_id );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
if ( strpos ( $message , '/program' ) === 0 ) {
$time = strtotime ( str_replace ( '/program ' , '' , $message ));
2019-10-28 19:26:45 +01:00
if ( $time === false ) {
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'peer' => $chat_id , 'message' => 'Invalid time provided' ]);
} elseif ( $time - time () <= 0 ) {
$this -> messages -> sendMessage ([ 'peer' => $chat_id , 'message' => 'Invalid time provided' ]);
2019-10-28 19:26:45 +01:00
} else {
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'peer' => $chat_id , 'message' => 'OK' ]);
2019-10-28 19:26:45 +01:00
$this -> programmed_call [] = [ $from_id , $time ];
2023-08-14 20:54:47 +02:00
$key = count ( $this -> programmed_call ) - 1 ;
Tools :: sleep ( $time - time ());
$this -> makeCall ( $from_id );
2019-10-28 19:26:45 +01:00
unset ( $this -> programmed_call [ $key ]);
}
}
2023-08-14 20:54:47 +02:00
} catch ( RPCErrorException $e ) {
2019-10-28 19:26:45 +01:00
try {
if ( $e -> rpc === 'USER_PRIVACY_RESTRICTED' ) {
$e = 'Please disable call privacy settings to make me call you' ;
} /* elseif ( strpos ( $e -> rpc , 'FLOOD_WAIT_' ) === 0 ) {
$t = str_replace ( 'FLOOD_WAIT_' , '' , $e -> rpc );
$e = " Too many people used the /call function. I'll be able to call you in $t seconds. \n You can also call me right now " ;
} */
2023-08-14 20:54:47 +02:00
$this -> messages -> sendMessage ([ 'peer' => $chat_id , 'message' => ( string ) $e ]);
} catch ( RPCErrorException $e ) {
2019-10-28 19:26:45 +01:00
}
$this -> logger ( $e );
2023-08-14 20:54:47 +02:00
} catch ( Exception $e ) {
2019-10-28 19:26:45 +01:00
$this -> logger ( $e );
}
}
2023-08-14 20:54:47 +02:00
public function onUpdateNewMessage ( $update ) : void
2019-10-28 19:26:45 +01:00
{
if ( $update [ 'message' ][ 'out' ] || $update [ 'message' ][ 'to_id' ][ '_' ] !== 'peerUser' || ! isset ( $update [ 'message' ][ 'from_id' ])) {
return ;
}
2020-11-27 17:08:29 +01:00
$this -> logger ( $update );
2023-08-14 20:54:47 +02:00
$chat_id = $from_id = $this -> getInfo ( $update )[ 'bot_api_id' ];
2019-10-28 19:26:45 +01:00
$message = $update [ 'message' ][ 'message' ] ? ? '' ;
2023-08-14 20:54:47 +02:00
$this -> handleMessage ( $chat_id , $from_id , $message );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
public function onUpdateNewEncryptedMessage ( $update ) : void
2019-10-28 19:26:45 +01:00
{
return ;
2023-08-14 20:54:47 +02:00
$chat_id = $this -> getInfo ( $update )[ 'InputEncryptedChat' ];
$from_id = $this -> getSecretChat ( $chat_id )[ 'user_id' ];
$message = $update [ 'message' ][ 'decrypted_message' ][ 'message' ] ? ? '' ;
$this -> handleMessage ( $chat_id , $from_id , $message );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
public function onUpdateEncryption ( $update ) : void
2019-10-28 19:26:45 +01:00
{
return ;
try {
if ( $update [ 'chat' ][ '_' ] !== 'encryptedChat' ) {
return ;
}
2023-08-14 20:54:47 +02:00
$chat_id = $this -> getInfo ( $update )[ 'InputEncryptedChat' ];
$from_id = $this -> getSecretChat ( $chat_id )[ 'user_id' ];
2019-10-28 19:26:45 +01:00
$message = '' ;
2023-08-14 20:54:47 +02:00
} catch ( Exception $e ) {
2019-10-28 19:26:45 +01:00
return ;
}
2023-08-14 20:54:47 +02:00
$this -> handleMessage ( $chat_id , $from_id , $message );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
#[Handler]
public function incomingCall ( VoIP & Incoming $voip ) : void
2019-10-28 19:26:45 +01:00
{
2023-08-15 10:08:47 +02:00
try {
$voip = $voip -> accept ();
} catch ( RPCErrorException $e ) {
if ( $e -> rpc === " CALL_PROTOCOL_COMPAT_LAYER_INVALID " ) {
$message -> reply ( " Please call me using Telegram Desktop, Telegram for Mac or Telegram Android! " );
return ;
}
throw $e ;
}
$this -> configureCall ( $voip );
2019-10-28 19:26:45 +01:00
}
2023-08-14 20:54:47 +02:00
public function __sleep () : array
2019-10-28 19:26:45 +01:00
{
return [ 'programmed_call' , 'my_users' ];
}
}
2023-08-14 20:54:47 +02:00
MyEventHandler :: startAndLoop ( 'magna.madeline' );