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 );
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\Message ;
2023-08-20 18:14:41 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\Ended ;
2023-08-15 10:15:20 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming ;
2023-08-20 18:23:28 +02:00
use danog\MadelineProto\EventHandler\SimpleFilter\Running ;
2023-08-14 20:54:47 +02:00
use danog\MadelineProto\Exception ;
use danog\MadelineProto\LocalFile ;
2023-08-20 14:48:34 +02:00
use danog\MadelineProto\Ogg ;
2023-08-20 18:14:41 +02:00
use danog\MadelineProto\ParseMode ;
2023-08-14 20:54:47 +02:00
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 MyEventHandler extends SimpleEventHandler
2019-10-28 19:26:45 +01:00
{
const ADMINS = [ 101374607 ]; // @danogentili, creator of MadelineProto
2023-08-20 18:14:41 +02:00
private array $programmed_call ;
private array $my_users ;
private string $me ;
/** @var array<int, VoIP> */
private array $calls = [];
/** @var array<int, int> */
private array $messageIds = [];
/** @var list<LocalFile> */
2023-08-20 14:48:34 +02:00
private array $songs = [];
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' );
2023-08-20 14:48:34 +02:00
$songs = glob ( '*ogg' );
if ( ! $songs ) {
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-20 18:14:41 +02:00
foreach ( $songs as & $song ) {
$song = new LocalFile ( $song );
2023-08-20 14:48:34 +02:00
try {
2023-08-20 18:14:41 +02:00
Ogg :: validateOgg ( $song );
2023-08-20 14:48:34 +02:00
} catch ( Throwable $e ) {
throw new AssertionError ( " An error occurred during validation of $song , please convert the file using convert.php or @libtgvoipbot! " , 0 , $e );
}
}
$this -> songs = $songs ;
2023-08-14 20:54:47 +02:00
$this -> programmed_call = [];
foreach ( $this -> programmed_call as $key => [ $user , $time ]) {
$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-20 18:14:41 +02:00
2023-08-15 10:15:20 +02:00
private int $lastLog = 0 ;
/**
* Handles updates to an in - progress broadcast .
*/
2023-08-20 18:14:41 +02:00
#[Handler]
public function broadcastProgress ( Progress $progress ) : void
2023-08-15 10:15:20 +02:00
{
if ( time () - $this -> lastLog > 5 || $progress -> status === Status :: FINISHED ) {
$this -> lastLog = time ();
$this -> sendMessageToAdmins (( string ) $progress );
}
}
2023-08-21 18:24:17 +02:00
#[FilterCommand('restart')]
public function restartCommand ( Message & FromAdmin $message ) : void
{
$this -> restart ();
}
2023-08-29 19:51:58 +02:00
2023-08-15 10:15:20 +02:00
#[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 ,
);
}
2023-08-29 19:51:58 +02:00
#[FilterCommand('bforward')]
public function broadcastForwardCommand ( 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 : false ,
pin : true ,
);
}
#[FilterCommand('skip')]
public function skipCommand ( Incoming & Message $message ) : void
{
$call = $this -> getCallByPeer ( $message -> chatId );
if ( ! $call ) {
$message -> reply ( " You're not currently in a call with me! " );
return ;
}
$call -> skip ();
}
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-20 18:14:41 +02:00
#[Cron(period: 10)]
public function statusLoop () : void
{
foreach ( $this -> calls as $user => $call ) {
if ( $call -> getCallState () === CallState :: ENDED ) {
unset ( $this -> calls [ $call -> otherID ], $this -> messageIds [ $call -> otherID ]);
continue ;
}
try {
$message = 'Total running calls: ' . count ( $this -> calls ) . PHP_EOL . PHP_EOL ;
$message .= PHP_EOL . PHP_EOL . PHP_EOL ;
2023-08-20 18:18:51 +02:00
$message .= " Emojis: " . implode ( '' , $call -> getVisualization () ? ? []);
2023-08-20 18:14:41 +02:00
$this -> messages -> editMessage ([ 'id' => $this -> messageIds [ $call -> otherID ], 'peer' => $user , 'message' => $message ]);
} catch ( RPCErrorException $e ) {
$this -> logger ( $e );
}
}
}
private function configureCall ( VoIP $call ) : void
2019-10-28 19:26:45 +01:00
{
2023-08-20 14:48:34 +02:00
$songs = $this -> songs ;
2023-08-29 19:51:58 +02:00
shuffle ( $songs );
2023-08-14 20:54:47 +02:00
$call -> playOnHold ( ... $songs );
2019-10-28 19:26:45 +01:00
}
2023-08-20 18:14:41 +02:00
private function makeCall ( int $user ) : void
2019-10-28 19:26:45 +01:00
{
try {
2023-08-20 18:14:41 +02:00
if ( $this -> getCallByPeer ( $user )) {
$this -> messages -> sendMessage ([ 'peer' => $user , 'message' => " I'm already in a call with you! " ]);
return ;
2019-10-28 19:26:45 +01:00
}
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 " ) {
2023-08-20 18:14:41 +02:00
$e = " Please call me using Telegram Desktop, Telegram for Mac or Telegram Android! " ;
2023-08-15 10:08:47 +02:00
}
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-20 18:14:41 +02:00
#[Handler]
public function handleMessage ( Incoming & Message $message ) : void
2019-10-28 19:26:45 +01:00
{
try {
2023-08-20 18:14:41 +02:00
$runCall = $message -> message === '/call' ;
if ( ! isset ( $this -> my_users [ $message -> chatId ]) || $message -> message === '/start' ) {
$runCall = true ;
$this -> my_users [ $message -> chatId ] = true ;
$message -> reply (
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 ! ) .
2023-08-29 19:54:42 +02:00
Use / skip to skip the current song .
2019-10-28 19:26:45 +01:00
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 !
2023-08-20 18:14:41 +02:00
" ,
parseMode : ParseMode :: MARKDOWN ,
noWebpage : true
);
2019-10-28 19:26:45 +01:00
}
2023-08-20 18:14:41 +02:00
if ( ! $this -> getCallByPeer ( $message -> chatId ) && $runCall && $message -> chatId > 0 ) {
$this -> makeCall ( $message -> chatId );
2019-10-28 19:26:45 +01:00
}
2023-08-20 18:14:41 +02:00
if ( strpos ( $message -> message , '/program' ) === 0 && $message -> chatId > 0 ) {
$time = strtotime ( str_replace ( '/program ' , '' , $message -> message ));
2019-10-28 19:26:45 +01:00
if ( $time === false ) {
2023-08-20 18:14:41 +02:00
$message -> reply ( 'Invalid time provided' );
2023-08-14 20:54:47 +02:00
} elseif ( $time - time () <= 0 ) {
2023-08-20 18:14:41 +02:00
$message -> reply ( 'Invalid time provided' );
2019-10-28 19:26:45 +01:00
} else {
2023-08-20 18:14:41 +02:00
$message -> reply ( 'OK' );
$this -> programmed_call [] = [ $message -> chatId , $time ];
2023-08-14 20:54:47 +02:00
$key = count ( $this -> programmed_call ) - 1 ;
Tools :: sleep ( $time - time ());
2023-08-20 18:14:41 +02:00
$this -> makeCall ( $message -> chatId );
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' ;
2023-08-20 18:14:41 +02:00
} elseif ( strpos ( $e -> rpc , 'FLOOD_WAIT_' ) === 0 ) {
2019-10-28 19:26:45 +01:00
$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-20 18:14:41 +02:00
}
$message -> reply (( string ) $e );
2023-08-14 20:54:47 +02:00
} 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
#[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 " ) {
2023-08-16 20:04:47 +02:00
$this -> messages -> sendMessage ( peer : $voip -> otherID , message : " Please call me using Telegram Desktop, Telegram for Mac or Telegram Android! " );
2023-08-15 10:08:47 +02:00
return ;
}
throw $e ;
}
$this -> configureCall ( $voip );
2019-10-28 19:26:45 +01:00
}
2023-08-20 18:14:41 +02:00
#[Handler]
public function endedCall ( VoIP & Ended $voip ) : void
{
unset ( $this -> calls [ $voip -> otherID ], $this -> messageIds [ $voip -> otherID ]);
2023-08-20 18:23:28 +02:00
}
2023-08-20 22:14:00 +02:00
#[Handler]
2023-08-20 18:23:28 +02:00
public function callRunning ( VoIP & Running $call ) : void
{
2023-08-20 22:25:28 +02:00
if ( isset ( $this -> calls [ $call -> otherID ])) {
return ;
}
2023-08-20 18:23:28 +02:00
try {
2023-08-20 22:16:09 +02:00
$message = 'Total running calls: ' . ( count ( $this -> calls ) + 1 ) . PHP_EOL . PHP_EOL ;
2023-08-20 18:23:28 +02:00
$message .= PHP_EOL . PHP_EOL . PHP_EOL ;
$message .= " Emojis: " . implode ( '' , $call -> getVisualization () ? ? []);
2023-08-20 18:14:41 +02:00
2023-08-20 22:15:42 +02:00
$this -> messageIds [ $call -> otherID ] = $this -> sendMessage ( peer : $call -> otherID , message : $message ) -> id ;
2023-08-20 18:23:28 +02:00
$this -> calls [ $call -> otherID ] = $call ;
} catch ( Throwable $e ) {
$this -> logger ( $e );
}
2023-08-20 18:14:41 +02:00
}
2023-08-14 20:54:47 +02:00
public function __sleep () : array
2019-10-28 19:26:45 +01:00
{
2023-08-20 18:14:41 +02:00
return [ 'programmed_call' , 'my_users' , 'messageIds' ];
2019-10-28 19:26:45 +01:00
}
}
2023-08-14 20:54:47 +02:00
MyEventHandler :: startAndLoop ( 'magna.madeline' );