Giter Site home page Giter Site logo

Comments (29)

Rost381 avatar Rost381 commented on July 25, 2024 2

Здравствуйте!
Я новичок в этом деле, прошу не закидывать камнями :)
При разработке Viber-бота на Python столкнулся с той же проблемой: повторение запросов через какое-то (случайное) время. Последовать совету Harier2008 не представляется возможным - время обработки зависит от серверов Viber и с моей стороны сократить его ни как не получается. Иногда посылка одной и той же простой клавиатуры обрабатывается 0,9 сек., иногда 25 (!!!) сек. Причем днем значительно медленней чем ночью.
Как писали авторы ранее, у одного и того же запроса, когда бы он посылался, одинаковые "message_token" и "timestamp". Решил для себя проблему так:
В самом начале бота создаю пустой список current_timestamp = [0], а дальше проверяю нет ли в нем timestamp текущего запроса. Если нет-добавляю, если есть - серверу 200

 viber_request = viber.parse_request(request.get_data().decode('utf8'))
 if isinstance(viber_request, ViberMessageRequest):
     message_timestamp = viber_request.timestamp # Извлекается timestamp
     if (current_timestamp.count(message_timestamp) != 0) : return Response(status=200) #Если этот запрос уже в списке
     if (len(current_timestamp)>=30) : del current_timestamp[0] #Удаление первого, если больше 30 записей
     current_timestamp.append(message_timestamp) #Добавление "timestamp" в список

Длина списка 30 взята эмпирически, с запасом. Обычно повторный запрос приходит в первой десятке и после повторного ответа серверу (2-40ms) уже не повторяется.

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024 1

@roman7722 К сожалению работающую версию кода уже удалил, но в целом, примерно так:

use Viber\Bot;
use Viber\Api\Sender;

$botSender = new Sender([
    'name' => $this->config['botName'],
    'avatar' => $this->config['avatar'],
]);

$apiKey = $this->config['apiKey'];
$bot = new Bot(['token' => $apiKey]);

$bot->getClient()->sendMessage(
    (new \Viber\Api\Message\Text())
        ->setSender($botSender)
        ->setReceiver(Yii::$app->request->post('user_id'))
        ->setText(Yii::$app->request->post('text'))
);

Туда просто отправляем post-запрос с параметрами user_id (получаем через $event->getSender()->getId()) и text (соответсвенно необходимое сообщение). Я отправлял запрос через GuzzleHttp.

from viber-bot-php.

fooger avatar fooger commented on July 25, 2024 1

Так это костыль

Viber это тоже сплошной костыль) Так как разработал для него крайне сложный бот, видел очень много его багов и приколов, могу с полной уверенностью сказать что продукт низкокачественный. А в целом, это единственное гарантированное решение, которое поможет с избежанием проблем повторного обрабатывания дублирующих сообщений. Бывает разное, и даже если Вашему скрипту не нужно много времени на обработку запроса, то может что-то заглючить в сети/на стороне вайбера/случится тормоза на стороне mysql/или вообще что угодно, и тогда придет дублирующий запрос. А это 100% поможет избежать любых проблем, связанной с повторной обработкой того же самого запроса.

from viber-bot-php.

Bogdaan avatar Bogdaan commented on July 25, 2024

Здравствуйте, данные сообщения приходят для каждого устройства (куда пришло сообщение). И у них соотвественно разные идентификаторы устройства.

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024

Оказалось, что проблема была во времени ответа. У меня сервер отвечал по 6.2+ секунды, а viber при отсутствии ответа через 5 секунд уже шлет повтор на сервер. Причем львиную долю этого времени занимал запрос отправки ответа пользователю (порядка 4-5 секунд). Решил проблему тем, что вынес отправку сообщения в очереди, время отклика сократилось до 200-600 мс.
Если кто-то столкнется с проблемой, то есть быстрое решение, а именно, вынести отправку в отдельный контроллер и просто посылать на него асинхронный post запрос.

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Уважаемый vedenskylx!
Не могли бы вы на кратеньком примере показать реализацию вашего быстрого решения, а именно как вынести отправку сообщения. Премного благодарен заранее.

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Александр (vedenskylx), спасибо за ответ, но я спрашивал про организацию асинхронного вызова php скрипта. Как сообщение отправить это-то понятно. Но собственно мне хватило вашего пояснения, что отправляли через GuzzleHttp. Я организовал отправку вот так, (если кому поможет):

$client = new \GuzzleHttp\Client(['base_uri' => 'https://example.com/viber/mybot/']);
$promise = $client->requestAsync('POST', 'sendPicture.php', [
     'form_params' => [
           'receiver' => urlencode($receiver),
           'answer' => urlencode($answer),
           'pic' => urlencode($pic),
     ]
]);
$promise->wait();

а вот и сам sendPicture.php:

<?php

require_once("vendor/autoload.php");

$config = require('./config.php');

use Viber\Bot;
use Viber\Api\Sender;

$botSender   = new Sender([
    'name'   => $config['senderName'],
    'avatar' => $config['senderAvatar']
]);

$bot = new Bot(['token' => $config['apiKey']]);

$receiver   = urldecode($_POST['receiver']);
$answer    = urldecode($_POST['answer']);
$pic           = urldecode($_POST['pic']);

$bot->getClient()->sendMessage(
    (new \Viber\Api\Message\Picture())
    ->setSender($botSender)
    ->setReceiver($receiver)
    ->setText($answer)
    ->setMedia($pic)
);

Но только вот не помогло мне это (, так и приходит в Viber по несколько картинок, и с текстовыми сообщениями такая-же ситуация.

В общем пока хз что делать (

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024

@roman7722 а через postman (или нечто подобное) запрос на hook сколько времени занимает?

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

vedenskylx ой, да мало, ну меньше 5 сек так точно. через 1,5-2 сек уже картинка прилетает

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Богдан вот говорит, что для каждого устройства сообщения отправляются (второй пост сверху). Но в других-то случаях всё правильно работает, а вот в некоторых местах дублируются (и 3 и 4 раза бывает повтор)

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

и нюанс еще с асинхронностью что-то не то, пока мой sendPicture.php не выполнится, дальше основной скрипт не работает.

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024

@roman7722 попробуйте таймауты добавить при отправке запроса. Если я не ошибаюсь, то так:

$promise = $client->requestAsync('POST', 'sendPicture.php', [
     'form_params' => [
           'receiver' => urlencode($receiver),
           'answer' => urlencode($answer),
           'pic' => urlencode($pic),
     ],
     'connect_timeout' => 1, 
     'timeout' => 1
]);

И еще я не уверен что нужна строчка:

$promise->wait();

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Нет Александр (vedenskylx), похоже, что асинхронность в Газл это миф.
Даже вот так, никакая асинхронность не работает:

$multicurl = new \GuzzleHttp\Handler\CurlMultiHandler();
$multihandler = \GuzzleHttp\HandlerStack::create($multicurl);
$client = new \GuzzleHttp\Client(['base_uri' => 'https://example.com/viber/mybot/', ['connect_timeout' => 3.14, 'handler' => $multihandler]]);
$promise = $client->requestAsync('POST', 'sendPicture.php', [
       'form_params' => [
            'receiver' => urlencode($receiver),
            'answer' => urlencode($answer),
            'pic' => urlencode($pic),
        ]
]);
$multicurl->tick();
$promise->wait();

а без $promise->wait(); вообще ничего не шлётся.

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024

@roman7722 Можно попробовать чистым curl'ом баз надстроек, с прерыванием запроса. Отправка запроса вернет ошибку, но запрос отправит. Нечто вроде:

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => "URL",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 1,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => "DATA",
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);
if ($err) {
    echo "cURL Error #:" . $err;
} else {
    echo $response;
}

Результат выполнения:

cURL Error #:Operation timed out after 1000 milliseconds with 0 bytes received

Status: 200 OK Time: 1151ms Size 516B

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Александр (vedenskylx), нашёл!
Ларчик. как говорится, открывался проще :)

Сообщения дублируются, если не указывать клавиатуру, а именно:

->setKeyboard(
        (new \Viber\Api\Keyboard())
        ->setButtons($document->getKeyset('today_yesterday_somedate'))
)

в моём случае.

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Но идея вынести отправку в отдельный скрипт, кстати, хорошая идея ). Потом легко бота переделать под другой мессенджер, когда добрые люди решат и Viber прикрыть, как телегу ))

from viber-bot-php.

vedenskylx avatar vedenskylx commented on July 25, 2024

@roman7722 Все таки, мне кажется, что дело не в клавиатуре. По ощущениям, проблема на серверах viber. Периодически запросы начинают летать, ответ приходит через секунду, а иногда по 11.
З.Ы. На всякий случай обновил и протестировал скрипт чистым курлом выше, он отправляет запрос и отваливается по таймауту через секунду.

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Ну да, сервера у них не стабильные, кстати, какие то.
А по поводу клавы хз, вродь пропала проблема, не задваиваются/затраиваются сообщения.

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

@vedenskylx Да, Александр, вы всё-таки правы, в одном месте манипуляции с клавой помогли, а в другом нет. ( Место в коде такое, что несколько сообщений нужно отправить, одно за одним, ибо не вмещается всё в одно (по мануалу 4000 символов максимально в одном сообщении можно отправлять пишут)

from viber-bot-php.

roman7722 avatar roman7722 commented on July 25, 2024

Исправил и во втором месте бота, где задваивались месседжи, установил предел размера текста в 2000 символов, теперь нормально шлются, без повторов.

Осталось побороть момент, где несколько картинок нужно отправить, тоже дубляж идёт. вместо 2-3х картинок отправляется 4-6. Пока решения нет.

from viber-bot-php.

fooger avatar fooger commented on July 25, 2024

Столкнулся с этой проблемой, и самостоятельно с ней разобрался. Дублирование происходит со стороны Viber, если не ответить на сообщение на протяжении 5 секунд. Т.е. ставим sleep(4) - все работает (и в Viber-е светится что сообщение доставлено), если sleep 5 (или чуть дольше) - даже если сервер принял/обработал сообщение/отправил ответ - Viber-у все равно , он в любом случае пришлет дубль через некоторое время (и в чате уже не светится что сообщение доставлено). Я изучил весь их алгоритм, но, к сожалению, так как это было примерно 2 месяца назад, уже подзабыл. Примерно (говорю по памяти, могут быть неточности), алгоритм такой:

  1. Если сервер получил сообщение и обрабатывает его больше 5 секунд - дублирование будет 100%. НО, сделает это viber в тот момент (вроде через секунду), как сервер даст ответ. Например: пришло сообщение на сервер, скрипт отвечает/молчит/... 65сек, в тот момент когда завершиться работа скрипта (и Viber увидит что сервер закончил работу) - происходит повторная отправка сообщения (и ему абсолютно все равно чем закончился предыдущий запрос)
  2. Такое, происходит примерно 3 раза. После этих нескольких раз, Viber уже делает паузу перед посылом следующего дубля - 1 минуту. Далее тот же алгоритм, он ожидает пока не ответит сервер (не помню сколько, но ожидает достаточно долго, вроде около минуты), и после этого берет паузу опять на 1 минуту. Если и следующий раз сервер не ответил вовремя - Viber перестает слать уведомления.
    Схема примерно следующая (повторюсь, могут быть неточности, так как уже всего не помню), это при условии если КАЖДЫЙ запрос происходит более чем 5 сек:
    Запрос на сервер/долгое ожидание ответа->после того как сервер закончил, повторный запрос сразу.
    Так вроде 3 раза.
    Потом пауза на 1 мин. Потом опять запрос на сервер/долгое ожидание ответа->после того как сервер закончил, повторный запрос через 1 минуту.
    На этом все.
    В общем, примерно как-то так, думаю общий принцип понятен.

Еще, вроде как, у Viber-а есть очередь сообщений. Если скрипт не обработает первое сообщение от пользователя (т.е. либо пока не ответит меньше чем за 5сек, или же пока Viber не сделает все свои попытки уведомить сервер), то второе он не получит.
Ну и опытным путем еще какие-то баги были (по моему, если за каким-то разом сообщение таки обработать вовремя, Viber этого не видит, и все равно "спамит"), или что-то в этом роде.
Поскольку для меня проблема с дублированием была критичной, то решил я его примерно так:

->on(function(Event $event){
	$token = (int)$event->getMessageToken();
	$message = $this->db->getRow("SELECT * FROM `duplicate_messages` WHERE `message_token`='{$token}'");
	if(isset($message)){
		//var_dump('delete duplicate message');
		exit;
	}

	$this->db->insert('duplicate_messages', ['message_token'=>$token, 'created_time'=>time()]);

	return false;
}, function (){})
->on(function(Event $event){
//остальные обработчики

ну и чистку бд по крону:

$db->query("DELETE FROM `duplicate_messages` WHERE `created_time`<".(time()-MIN*10));

Если что не понятно - спрашивайте, отвечу/объясню.

from viber-bot-php.

gfdv avatar gfdv commented on July 25, 2024

48769e2c5067d493-f8c18d0c809d0d95-b23d553fa6244d4f

from viber-bot-php.

gfdv avatar gfdv commented on July 25, 2024

48769e2c5067d493-f8c18d0c809d0d95-b23d553fa6244d4f

from viber-bot-php.

aiera1993 avatar aiera1993 commented on July 25, 2024

fooger, красавчик!

from viber-bot-php.

Harier2008 avatar Harier2008 commented on July 25, 2024

Столкнулся с этой проблемой, и самостоятельно с ней разобрался.

Исследование отличное! Спасибо.
Решение тоже интересное, но решив проблему с дублированием - останется проблема с тем, что сообщение будет отображаться как не доставленное. Да и постоянный спам от серверов viber на сервер тоже не очень хорошо.

Я решил проблему иначе. Тоже скорее всего костыль, но работает :)

Мне нужно было по запросам от клиента получать данные из базы и отправлять ему ответ.
Собственно как раз время на получение данных превышало эти злополучные 5 секунд, поэтому я просто в onText получал Viber ID (getSender()->getId()) отправителя и текст (getMessage()->getText()) и эти данные отправлял при помощи чистого CURL на url отдельного скрипта, который уже выполнял всю работу с БД, а дальше после выполнения запроса в БД выполнял sendMessage c этими данными на полученный Viber ID.

from viber-bot-php.

lerinoi avatar lerinoi commented on July 25, 2024

Данная проблема по прежнему актуальна, есть у кого-нибудь нормальное решение?

from viber-bot-php.

EdgarHubert avatar EdgarHubert commented on July 25, 2024

Ну смотрите. Скрипт, который вы пищите, выполняется каждый раз, когда на ваш сервер приходит сообщение. В случае, если за 5 секунд сообщение не прошло, повторяется дублирование. Про это уже выше писали.

У всех этих повторяющихся сообщений один и тот же идентификатор. я его получаю так:

$token = (int)$event->getMessageToken();

Дальше у меня есть отдельная таблица в БД для контроля дублирования сообщений. Я проверяю, есть ли у меня запись о получении этого сообщения. Если нет, то я добавляю эту запись. если есть - прерываю скрипт.

$token_control = mysql_num_rows(mysql_query("SELECT 1 FROM `duplicate_messages` WHERE message_id = '$token'"));
					
$date_msg=date("Y-m-d H:i:s");
					
IF( $token_control == 0 ) 
	{
		mysql_query("INSERT INTO `duplicate_messages` (user_id, message_id, message_time) VALUES ('$receiverId', '$token', '$date_msg')");
	} 
ELSE 
	{
		exit;
	}

Чтобы не хранить информацию о сообщениях, которая не актуальна, делаю так:

$time_left = date("Y/m/d H:i:s", mktime(date("H"), date("i")-10, date("s"), date("m") , date("d"), date("Y")));	
mysql_query("DELETE FROM duplicate_messages WHERE message_time < '$time_left'");

и все это обрабатывается внутри ->onText, которая у меня одна на весь скрипт. Я беру из нее текст, присланный пользователем и уже внутри обрабатываю, как мне надо.

$onText_buffer = $event->getMessage()->getText();

from viber-bot-php.

lerinoi avatar lerinoi commented on July 25, 2024

Ну смотрите. Скрипт, который вы пищите, выполняется каждый раз, когда на ваш сервер приходит сообщение. В случае, если за 5 секунд сообщение не прошло, повторяется дублирование. Про это уже выше писали.

У всех этих повторяющихся сообщений один и тот же идентификатор. я его получаю так:

$token = (int)$event->getMessageToken();

Дальше у меня есть отдельная таблица в БД для контроля дублирования сообщений. Я проверяю, есть ли у меня запись о получении этого сообщения. Если нет, то я добавляю эту запись. если есть - прерываю скрипт.

$token_control = mysql_num_rows(mysql_query("SELECT 1 FROM `duplicate_messages` WHERE message_id = '$token'"));
					
$date_msg=date("Y-m-d H:i:s");
					
IF( $token_control == 0 ) 
	{
		mysql_query("INSERT INTO `duplicate_messages` (user_id, message_id, message_time) VALUES ('$receiverId', '$token', '$date_msg')");
	} 
ELSE 
	{
		exit;
	}

Чтобы не хранить информацию о сообщениях, которая не актуальна, делаю так:

$time_left = date("Y/m/d H:i:s", mktime(date("H"), date("i")-10, date("s"), date("m") , date("d"), date("Y")));	
mysql_query("DELETE FROM duplicate_messages WHERE message_time < '$time_left'");

и все это обрабатывается внутри ->onText, которая у меня одна на весь скрипт. Я беру из нее текст, присланный пользователем и уже внутри обрабатываю, как мне надо.

$onText_buffer = $event->getMessage()->getText();

Так это костыль

from viber-bot-php.

Harier2008 avatar Harier2008 commented on July 25, 2024

Делайте так чтобы ответ на сервера вайбер гарантированно был отдан менее чем за 5 секунд и никаких костылей не потребуется.

from viber-bot-php.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.