This commit is contained in:
danogentili 2017-02-21 21:04:37 +03:00
parent ec5e23131e
commit 3154b97763
11 changed files with 223 additions and 306 deletions

173
bots/MadelineProto_bot.php Executable file
View File

@ -0,0 +1,173 @@
#!/usr/bin/env php
<?php
/*
Copyright 2016-2017 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/>.
*/
require 'vendor/autoload.php';
$settings = [];
$MadelineProto = false;
try {
$MadelineProto = \danog\MadelineProto\Serialization::deserialize('MadelineProto_bot.madeline');
} catch (\danog\MadelineProto\Exception $e) {
}
if (file_exists('token.php') && $MadelineProto === false) {
include_once 'token.php';
$MadelineProto = new \danog\MadelineProto\API($settings);
$authorization = $MadelineProto->bot_login($MadelineProto_token);
\danog\MadelineProto\Logger::log([$authorization], \danog\MadelineProto\Logger::NOTICE);
}
$reply_markup = ['inline_keyboard' =>
[
[ // Row 1
['text' => 'Row 1 b1'],
['text' => 'Row 1 b2'],
['text' => 'Row 1 b3'],
],
[ // Row 2
['text' => 'Row 2 b1'],
['text' => 'Row 2 b2'],
['text' => 'Row 2 b3'],
],
[ // Row 3
['text' => 'Row 3 b1'],
['text' => 'Row 3 b2'],
['text' => 'Row 3 b3'],
],
]
];
$start = "This bot can create inline text buttons.
To use it, simply type an inline query with the following syntax:
Row 1 b1 | Row 1 b2 | Row 1 b3
Row 2 b1 | Row 2 b2 | Row 2 b3
Row 3 b1 | Row 3 b2 | Row 3 b3
This will create a keyboard exactly like the one used in this message (click the buttons ;D)
Created by [Daniil Gentili](mention:@danogentili) (@daniilgentili) using the [MadelineProto PHP MTProto client](daniil.it/MadelineProto).";
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {
case 'updateNewMessage':
if (isset($update['update']['message']['out']) && $update['update']['message']['out']) {
continue;
}
try {
if (preg_match('|/start|', $update['update']['message']['message'])) {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['from_id'], 'message' => $start, 'reply_to_msg_id' => $update['update']['message']['id'], 'parse_mode' => 'markdown', 'reply_markup' => $reply_markup]);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
break;
case 'updateNewChannelMessage':
if (isset($update['update']['message']['out']) && $update['update']['message']['out']) {
continue;
}
try {
if (preg_match('|/start|', $update['update']['message']['message'])) {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['to_id'], 'message' => $start, 'reply_to_msg_id' => $update['update']['message']['id'], 'parse_mode' => 'markdown', 'reply_markup' => $reply_markup]);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
break;
case 'updateBotInlineQuery':
try {
$sswitch = ['_' => 'inlineBotSwitchPM', 'text' => 'FAQ', 'start_param' => 'lel'];
if ($update['update']['query'] === '') {
$MadelineProto->messages->setInlineBotResults(['query_id' => $update['update']['query_id'], 'results' => [], 'cache_time' => 0, 'switch_pm' => $sswitch]);
} else {
$toset = ['query_id' => $update['update']['query_id'], 'results' => [], 'cache_time' => 0, 'private' => true];
if (preg_match('|\$\s*$|', $update['update']['query'])) {
$exploded = explode('|', preg_replace('/\$\s*$/', '', $update['update']['query']));
array_walk($exploded, function (&$value, $key) {
$value = preg_replace(['/^\s+/', '/\s+$/'], '', $value);
});
$query = array_shift($exploded);
foreach ($exploded as $current => $botq) {
$bot = preg_replace('|:.*|', '', $botq);
if ($bot === '' || $uMadelineProto->get_info($bot)['bot_api_id'] === $MadelineProto->API->datacenter->authorization['user']['id']) {
$toset['switch_pm'] = $sswitch;
break;
}
$select = preg_replace('|'.$bot.':|', '', $botq);
$results = $uMadelineProto->messages->getInlineBotResults(['bot' => $bot, 'peer' => $update['update']['user_id'], 'query' => $query, 'offset' => $offset]);
if (isset($results['switch_pm'])) {
$toset['switch_pm'] = $results['switch_pm'];
break;
}
$toset['gallery'] = $results['gallery'];
$toset['results'] = [];
if (is_numeric($select)) {
$toset['results'][0] = $results['results'][$select - 1];
} elseif ($select === '') {
$toset['results'] = $results['results'];
} else {
foreach ($results['results'] as $result) {
if (isset($result['send_message']['message']) && preg_match('|'.$select.'|', $result['send_message']['message'])) {
$toset['results'][0] = $result;
}
}
}
if (!isset($toset['results'][0])) {
$toset['results'] = $results['results'];
}
if (count($exploded) - 1 === $current || !isset($toset['results'][0]['send_message']['message'])) {
break;
}
$query = $toset['results'][0]['send_message']['message'];
}
}
if (empty($toset['results'])) {
$toset['switch_pm'] = $sswitch;
} else {
array_walk($toset['results'], 'translate');
}
$MadelineProto->messages->setInlineBotResults($toset);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
try {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['user_id'], 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) {
}
try {
$toset['switch_pm'] = $sswitch;
$MadelineProto->messages->setInlineBotResults($toset);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) {
}
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
try {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['user_id'], 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) {
}
try {
$toset['switch_pm'] = $sswitch;
$MadelineProto->messages->setInlineBotResults($toset);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) {
}
}
}
}
\danog\MadelineProto\Serialization::serialize('MadelineProto_bot.madeline', $MadelineProto);
}

View File

@ -15,7 +15,7 @@ require 'vendor/autoload.php';
$settings = [];
$MadelineProto = false;
try {
$MadelineProto = \danog\MadelineProto\Serialization::deserialize('bot.madeline');
$MadelineProto = \danog\MadelineProto\Serialization::deserialize('pipesbot.madeline');
} catch (\danog\MadelineProto\Exception $e) {
}
$uMadelineProto = \danog\MadelineProto\Serialization::deserialize('pwr.madeline');
@ -92,9 +92,9 @@ $offset = 0;
$start = "This bot can create a pipeline between inline bots.
To use it, simply type an inline query with the following syntax:
Query | @ainlinebot:1 | @binlinebot:lel | @inlinebot \$
@pipesbot Hey I'm writing this using the leet filter of @filtersbot w/ @lolcatzbot | @filtersbot:eleet | @lolcatzbot \$
This will make an inline query with text \"Query\" to @ainlinebot, take the first result if it's a text message (entities will be ignored, if it's a media message you will be redirected here), then it will make an inline query to @binlinebot with the text received out of the first bot, select the result that is a text message with the word \"lel\" in it (regexes are supported), and finally pipe it to @inlinebot, fetch all results and return them to you.
This will make an inline query with text \"Hey I'm writing this using the leet filter of @filtersbot w/ @lolcatzbot\" to @filtersbot, take the result that has the word \"eleet\" (regexes are supported. you can specify just the username to select the first result), in the title, if it's a text message (entities will be ignored, if it's a media message you will be redirected here), then it will make an inline query to @lolcatzbot with the text received out of the first bot fetch all results and return them to you.
Note that the query must be terminated by a \$
Created by @danogentili (@daniilgentili) using the daniil.it/MadelineProto PHP MTProto client.";

View File

@ -15,6 +15,7 @@
"require": {
"php": ">=5.6.0",
"danog/phpstruct": "dev-fast",
"danog/primemodule": "dev-master",
"phpseclib/phpseclib": "dev-ige",
"vlucas/phpdotenv": "^2.4",
"krakjoe/pthreads-polyfill": "dev-master",

View File

@ -15,7 +15,7 @@ namespace danog\MadelineProto;
/**
* Manages all of the mtproto stuff.
*/
class MTProto extends PrimeModule
class MTProto
{
use \danog\MadelineProto\MTProtoTools\AckHandler;
use \danog\MadelineProto\MTProtoTools\AuthKeyHandler;

View File

@ -83,16 +83,14 @@ trait AuthKeyHandler
* ***********************************************************************
* Compute p and q
*/
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
list($p, $q) = $this->PrimeFactors($pq);
$p = new \phpseclib\Math\BigInteger($p);
$q = new \phpseclib\Math\BigInteger($q);
if ($p->compare($q) > 0) {
$pq = \danog\PHP\Struct::unpack('>Q', $pq_bytes)[0];
$p = \danog\PrimeModule::auto_single($pq);
$q = $pq / $p;
if ($p > $q) {
list($p, $q) = [$q, $p];
}
if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) {
if ($pq !== $p*$q) {
throw new \danog\MadelineProto\Exception("couldn't compute p and q.");
}
@ -102,8 +100,8 @@ trait AuthKeyHandler
* ***********************************************************************
* Serialize object for req_DH_params
*/
$p_bytes = \danog\PHP\Struct::pack('>I', (string) $p);
$q_bytes = \danog\PHP\Struct::pack('>I', (string) $q);
$p_bytes = \danog\PHP\Struct::pack('>I', $p);
$q_bytes = \danog\PHP\Struct::pack('>I', $q);
$new_nonce = $this->random(32);

View File

@ -1,135 +0,0 @@
<?php
/*
Copyright 2016-2017 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 the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class PrimeModule
{
// Uses https://github.com/LonamiWebs/Telethon/blob/master/telethon/crypto/factorizator.py, thank you so freaking much!
public function find_small_multiplier_lopatin($what)
{
$g = 0;
for ($i = 0; $i < 3; $i++) {
$q = (rand(0, 127) & 15) + 17;
$x = rand(0, 1000000000) + 1;
$y = $x;
$lim = 1 << ($i + 18);
for ($j = 1; $j <= $lim; $j++) {
list($a, $b, $c) = [$x, $x, $q];
while ($b != 0) {
if (($b & 1) != 0) {
$c += $a;
if ($c >= $what) {
$c -= $what;
}
}
$a += $a;
if ($a >= $what) {
$a -= $what;
}
$b >>= 1;
}
$x = $c;
$z = ($x < $y) ? $y - $x : $x - $y;
$g = $this->gcd($z, $what);
if ($g != 1) {
break;
}
if (($j & ($j - 1)) === 0) {
$y = $x;
}
}
if ($g > 1) {
break;
}
}
$p = $what; // g
return min($p, $g);
}
public function gcd($a, $b)
{
while ($a != 0 && $b != 0) {
while ($b & 1 === 0) {
$b >>= 1;
}
while ($a & 1 === 0) {
$a >>= 1;
}
if ($a > $b) {
$a -= $b;
} else {
$b -= $a;
}
}
return ($b === 0) ? $a : $b;
}
public function PrimeFactors($pq)
{
$pqstr = (string) $pq;
\danog\MadelineProto\Logger::log(['Trying to use the python factorization module'], \danog\MadelineProto\Logger::VERBOSE);
if (function_exists('shell_exec')) {
try {
$res = json_decode(shell_exec('python '.__DIR__.'/getpq.py '.$pqstr));
if (count($res) === 2) {
return $res;
}
} catch (Exception $e) {
}
}
\danog\MadelineProto\Logger::log(['Trying to use the wolfram alpha factorization module'], \danog\MadelineProto\Logger::VERBOSE);
try {
$query = 'Do prime factorization of '.$pqstr;
$params = [
'async' => true,
'banners' => 'raw',
'debuggingdata' => false,
'format' => 'moutput',
'formattimeout' => 8,
'input' => $query,
'output' => 'JSON',
'proxycode' => json_decode(file_get_contents('http://www.wolframalpha.com/api/v1/code'), true)['code'],
];
$url = 'https://www.wolframalpha.com/input/json.jsp?'.http_build_query($params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Referer: https://www.wolframalpha.com/input/?i='.urlencode($query)]);
curl_setopt($ch, CURLOPT_URL, $url);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($res['queryresult']['pods'] as $cur) {
if ($cur['id'] === 'Divisors') {
$res = explode(', ', preg_replace(["/{\d+, /", "/, \d+}$/"], '', $cur['subpods'][0]['moutput']));
break;
}
}
if (count($res) === 2) {
return $res;
}
} catch (Exception $e) {
}
\danog\MadelineProto\Logger::log(['Trying to use the native factorization module'], \danog\MadelineProto\Logger::VERBOSE);
$res = $this->find_small_multiplier_lopatin((int) $pqstr);
$res = [$res, $pqstr / $res];
if ($res[1] != 1) {
return $res;
}
throw new Exception("Couldn't calculate pq!");
}
}

View File

@ -115,6 +115,13 @@ trait Login
return $this->API->datacenter->authorization = $this->account->getPassword();
}
if ($e->getMessage() === 'PHONE_NUMBER_UNOCCUPIED') {
\danog\MadelineProto\Logger::log(['An account has not been created for this number, you will have to call the complete_signup function...'], \danog\MadelineProto\Logger::NOTICE);
$this->API->datacenter->login_temp_status = 'waiting_signup';
$this->API->should_serialize = true;
$this->API->datacenter->authorization['phone_code'] = $code;
return ['_' => 'account.needSignup'];
}
throw $e;
}
$this->API->datacenter->authorization = $authorization;
@ -127,6 +134,31 @@ trait Login
return $this->API->datacenter->authorization;
}
public function complete_signup($first_name, $last_name)
{
if ($this->API->datacenter->login_temp_status !== 'waiting_signup') {
throw new \danog\MadelineProto\Exception("I'm not waiting for the password! Please call the phone_login and the complete_phone_login methods first!");
}
$this->API->datacenter->login_temp_status = 'none';
\danog\MadelineProto\Logger::log(['Signing up as a normal user...'], \danog\MadelineProto\Logger::NOTICE);
$this->API->datacenter->authorization = $this->API->method_call(
'auth.signUp',
[
'phone_number' => $this->API->datacenter->authorization['phone_number'],
'phone_code_hash' => $this->API->datacenter->authorization['phone_code_hash'],
'phone_code' => $this->API->datacenter->authorization['phone_code'],
'first_name' => $first_name,
'last_name' => $last_name,
]
);
$this->API->datacenter->authorized = true;
$this->API->get_updates_state();
$this->API->should_serialize = true;
\danog\MadelineProto\Logger::log(['Signed up in successfully!'], \danog\MadelineProto\Logger::NOTICE);
return $this->API->datacenter->authorization;
}
public function complete_2fa_login($password)
{
if ($this->API->datacenter->login_temp_status !== 'waiting_password') {
@ -143,9 +175,7 @@ trait Login
$this->API->datacenter->authorized = true;
$this->API->get_updates_state();
$this->API->should_serialize = true;
\danog\MadelineProto\Logger::log(['Logged in successfully!'], \danog\MadelineProto\Logger::NOTICE);
return $this->API->datacenter->authorization;
}
}

View File

@ -1,14 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
import prime
import sys
import json
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
pq = prime.primefactors(long(sys.argv[1]))
sys.stdout.write(json.dumps(pq))
sys.stdout.flush()

View File

@ -1,141 +0,0 @@
# NOTICE!!! This is copied from https://stackoverflow.com/questions/4643647/fast-prime-factorization-module
import random
def primesbelow(N):
# http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
#""" Input N>=6, Returns a list of primes, 2 <= p < N """
correction = N % 6 > 1
N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6]
sieve = [True] * (N // 3)
sieve[0] = False
for i in range(int(N ** .5) // 3 + 1):
if sieve[i]:
k = (3 * i + 1) | 1
sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1)
sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1)
return [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]]
smallprimeset = set(primesbelow(100000))
_smallprimeset = 100000
def isprime(n, precision=7):
# http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time
if n == 1 or n % 2 == 0:
return False
elif n < 1:
raise ValueError("Out of bounds, first argument must be > 0")
elif n < _smallprimeset:
return n in smallprimeset
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
for repeat in range(precision):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for r in range(s - 1):
x = pow(x, 2, n)
if x == 1: return False
if x == n - 1: break
else: return False
return True
# https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
def pollard_brent(n):
if n % 2 == 0: return 2
if n % 3 == 0: return 3
y, c, m = random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1)
g, r, q = 1, 1, 1
while g == 1:
x = y
for i in range(r):
y = (pow(y, 2, n) + c) % n
k = 0
while k < r and g==1:
ys = y
for i in range(min(m, r-k)):
y = (pow(y, 2, n) + c) % n
q = q * abs(x-y) % n
g = gcd(q, n)
k += m
r *= 2
if g == n:
while True:
ys = (pow(ys, 2, n) + c) % n
g = gcd(abs(x - ys), n)
if g > 1:
break
return g
smallprimes = primesbelow(10000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
factors = []
limit = int(n ** .5) + 1
for checker in smallprimes:
if checker > limit: break
while n % checker == 0:
factors.append(checker)
n //= checker
limit = int(n ** .5) + 1
if checker > limit: break
if n < 2: return factors
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
def factorization(n):
factors = {}
for p1 in primefactors(n):
try:
factors[p1] += 1
except KeyError:
factors[p1] = 1
return factors
totients = {}
def totient(n):
if n == 0: return 1
try: return totients[n]
except KeyError: pass
tot = 1
for p, exp in factorization(n).items():
tot *= (p - 1) * p ** (exp - 1)
totients[n] = tot
return tot
def gcd(a, b):
if a == b: return a
while b > 0: a, b = b, a % b
return a
def lcm(a, b):
return abs(a * b) // gcd(a, b)

View File

@ -54,6 +54,10 @@ if ($MadelineProto === false) {
\danog\MadelineProto\Logger::log(['2FA is enabled'], \danog\MadelineProto\Logger::NOTICE);
$authorization = $MadelineProto->complete_2fa_login(readline('Please enter your password (hint '.$authorization['hint'].'): '));
}
if ($authorization['_'] === 'account.needSignup') {
\danog\MadelineProto\Logger::log(['Registering new user'], \danog\MadelineProto\Logger::NOTICE);
$authorization = $MadelineProto->complete_signup($code, readline('Please enter your first name: '), readline('Please enter your last name (can be empty): '));
}
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('session.madeline', $MadelineProto).' bytes'.PHP_EOL;
} else {
@ -62,7 +66,8 @@ if ($MadelineProto === false) {
}
$message = (getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd)' : ('Travis ci tests in progress: commit '.getenv('TRAVIS_COMMIT').', job '.getenv('TRAVIS_JOB_NUMBER').', PHP version: '.getenv('TRAVIS_PHP_VERSION'));
$flutter = 'https://storage.pwrtelegram.xyz/pwrtelegrambot/document/file_6570.mp4';
var_dump( $MadelineProto->API->get_full_info('@pwrtelegram'));
$mention = $MadelineProto->get_info(getenv('TEST_USERNAME')); // Returns an array with all of the constructors that can be extracted from a username or an id
$mention = $mention['user_id']; // Selects only the numeric user id