Commit b14742a0 authored by MBoretto's avatar MBoretto

Merge branch 'master' into feature/logger

parents b1184d86 999ee2e7
sudo: false
language: php
php:
......@@ -6,7 +8,17 @@ php:
- 7.0
- hhvm
sudo: false
matrix:
allow_failures:
- php: hhvm
fast_finish: true
notifications:
on_success: never
on_failure: always
git:
depth: 1
before_install:
- composer self-update
......@@ -14,17 +26,9 @@ before_install:
install:
- travis_retry composer install --no-interaction
before_script:
- mysql -e 'create database telegrambot; use telegrambot; source structure.sql;'
script:
- vendor/bin/phpunit
- sh -c "if [ '$TRAVIS_PHP_VERSION' != '7.0' ]; then vendor/bin/phpcs --report=full --extensions=php -np --standard=build/phpcs .; fi"
matrix:
allow_failures:
- php: hhvm
- php: 7.0
fast_finish: true
notifications:
on_success: never
on_failure: always
- vendor/bin/phpcs --report=full --extensions=php -np --standard=build/phpcs .
......@@ -28,7 +28,8 @@ The Bot can:
- handle commands in chat with other bots.
- manage Channel from the bot admin interface.
- full support for **inline bots**. (**new!**)
- Messages, InlineQuery and ChosenInlineQuery are stored in the Database. (**new!**)
- Messages, InlineQuery and ChosenInlineQuery are stored in the Database.
- Conversation feature (**new!**)
-----
This code is available on
......@@ -348,7 +349,7 @@ You can also store inline query and chosen inline query in the database.
### Channels Support
All methods implemented can be used to manage channels.
With [admin commands](#admin-commands) you can manage your channel directly with your bot private chat.
With [admin commands](#admin-commands) you can manage your channels directly with your bot private chat.
### Commands
......@@ -397,7 +398,7 @@ $telegram->setCommandConfig('date', ['google_api_key' => 'your_google_api_key_he
Enabling this feature, the admin bot can perform some super user commands like:
- Send message to all chats */sendtoall*
- List all the chats started with the bot */chats*
- Send a message to a channel */sendtochannel* (NEW! see below how to configure it)
- Post any content to your channels */sendtochannel* (NEW! see below how to configure it)
You can specify one or more admins with this option:
```php
......@@ -414,7 +415,11 @@ To enable this feature follow these steps:
- Enable admin interface for your user as explained in the admin section above.
- Enter your channel name as a parameter for the */sendtochannel* command:
```php
$telegram->setCommandConfig('sendtochannel', ['your_channel' => '@type_here_your_channel']);
$telegram->setCommandConfig('sendtochannel', ['your_channel' => ['@type_here_your_channel']]);
```
- If you want to manage more channels:
```php
$telegram->setCommandConfig('sendtochannel', ['your_channel'=>['@type_here_your_channel', '@type_here_another_channel', '@and_so_on']]);
```
- Enjoy!
......
<?php
/*
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* Written by <marco.bore@gmail.com>
*/
namespace Longman\TelegramBot\Commands;
*/
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Command;
use Longman\TelegramBot\Entities\Update;
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Entities\ForceReply;
class ForceReplyCommand extends Command
/**
* User "/forcereply" command
*/
class ForceReplyCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'forcereply';
protected $description = 'Force reply with reply markup';
protected $usage = '/forcereply';
protected $version = '0.0.5';
protected $enabled = true;
/**#@-*/
/**
* {@inheritdoc}
*/
public function execute()
{
$update = $this->getUpdate();
$message = $this->getMessage();
$message_id = $message->getMessageId();
$chat_id = $message->getChat()->getId();
$text = $message->getText(true);
$data = array();
$data = [];
$data['chat_id'] = $chat_id;
$data['text'] = 'Write something:';
#$data['reply_to_message_id'] = $message_id;
$force_reply = new ForceReply(['selective' => false]);
#echo $json;
$data['reply_markup'] = $force_reply;
$data['reply_markup'] = new ForceReply(['selective' => false]);
$result = Request::sendMessage($data);
return $result;
return Request::sendMessage($data);
}
}
<?php
/*
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* Written by Marco Boretto <marco.bore@gmail.com>
*/
namespace Longman\TelegramBot\Commands;
*/
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Command;
use Longman\TelegramBot\Entities\Update;
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
use Longman\TelegramBot\Entities\ForceReply;
class HidekeyboardCommand extends Command
/**
* User "/hidekeyboard" command
*/
class HidekeyboardCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'hidekeyboard';
protected $description = 'Hide the custom keyboard';
protected $usage = '/hidekeyboard';
protected $version = '0.0.5';
protected $enabled = true;
/**#@-*/
/**
* {@inheritdoc}
*/
public function execute()
{
$update = $this->getUpdate();
$message = $this->getMessage();
$message_id = $message->getMessageId();
$chat_id = $message->getChat()->getId();
$text = $message->getText(true);
$data = array();
$data = [];
$data['chat_id'] = $chat_id;
$data['text'] = 'Keyboard Hided';
#$data['reply_to_message_id'] = $message_id;
$reply_keyboard_hide = new ReplyKeyboardHide([ 'selective' => false]);
$data['reply_markup'] = $reply_keyboard_hide;
$data['reply_markup'] = new ReplyKeyboardHide([ 'selective' => false]);
$result = Request::sendMessage($data);
return $result;
return Request::sendMessage($data);
}
}
<?php
/*
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Commands;
*/
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Command;
use Longman\TelegramBot\Entities\Update;
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
class ImageCommand extends Command
/**
* User "/image" command
*/
class ImageCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'image';
protected $description = 'Send Image';
protected $usage = '/image';
protected $version = '1.0.0';
protected $enabled = true;
protected $public = true;
/**#@-*/
/**
* {@inheritdoc}
*/
public function execute()
{
$update = $this->getUpdate();
$message = $this->getMessage();
$chat_id = $message->getChat()->getId();
$text = $message->getText(true);
$data = array();
$data = [];
$data['chat_id'] = $chat_id;
$data['caption'] = $text;
//$result = Request::sendDocument($data,'structure.sql');
//$result = Request::sendSticker($data, $this->telegram->getUploadPath().'/'.'image.jpg');
$result = Request::sendPhoto($data, $this->telegram->getUploadPath().'/'.'image.jpg');
return $result;
return Request::sendPhoto($data, $this->telegram->getUploadPath().'/'.'image.jpg');
}
}
<?php
/*
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* written by Marco Boretto <marco.bore@gmail.com>
*/
namespace Longman\TelegramBot\Commands;
*/
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Command;
use Longman\TelegramBot\Entities\Update;
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
use Longman\TelegramBot\Entities\ForceReply;
class KeyboardCommand extends Command
/**
* User "/keyboard" command
*/
class KeyboardCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'keyboard';
protected $description = 'Show a custom keybord with reply markup';
protected $usage = '/keyboard';
protected $version = '0.0.5';
protected $enabled = true;
/**#@-*/
/**
* {@inheritdoc}
*/
public function execute()
{
$update = $this->getUpdate();
$message = $this->getMessage();
$message_id = $message->getMessageId();
$chat_id = $message->getChat()->getId();
$text = $message->getText(true);
$data = array();
$data = [];
$data['chat_id'] = $chat_id;
$data['text'] = 'Press a Button:';
#$data['reply_to_message_id'] = $message_id;
#Keyboard examples
$keyboards = array();
//Keyboard examples
$keyboards = [];
//0
$keyboard[] = ['7','8','9'];
......@@ -64,7 +62,6 @@ class KeyboardCommand extends Command
$keyboards[] = $keyboard;
unset($keyboard);
//2
$keyboard[] = ['A'];
$keyboard[] = ['B'];
......@@ -73,8 +70,6 @@ class KeyboardCommand extends Command
$keyboards[] = $keyboard;
unset($keyboard);
//3
$keyboard[] = ['A'];
$keyboard[] = ['B'];
......@@ -83,8 +78,7 @@ class KeyboardCommand extends Command
$keyboards[] = $keyboard;
unset($keyboard);
$reply_keyboard_markup = new ReplyKeyboardMarkup(
$data['reply_markup'] = new ReplyKeyboardMarkup(
[
'keyboard' => $keyboards[1] ,
'resize_keyboard' => true,
......@@ -92,10 +86,7 @@ class KeyboardCommand extends Command
'selective' => false
]
);
#echo $json;
$data['reply_markup'] = $reply_keyboard_markup;
$result = Request::sendMessage($data);
return $result;
return Request::sendMessage($data);
}
}
......@@ -159,7 +159,6 @@ abstract class Command
return Request::sendMessage($data);
}
/**
* Get update object
*
......
......@@ -10,6 +10,8 @@
namespace Longman\TelegramBot\Commands\SystemCommands;
use Longman\TelegramBot\Conversation;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Commands\SystemCommand;
/**
......@@ -22,14 +24,38 @@ class GenericmessageCommand extends SystemCommand
*/
protected $name = 'Genericmessage';
protected $description = 'Handle generic message';
protected $version = '1.0.1';
protected $version = '1.0.2';
protected $need_mysql = true;
/**#@-*/
/**
* {@inheritdoc}
* Execution if MySQL is required but not available
*
* @return boolean
*/
public function executeNoDB()
{
//Do nothing
return Request::emptyResponse();
}
/**
* Execute command
*
* @return boolean
*/
/*public function execute()
public function execute()
{
//If a conversation is busy, execute the conversation command after handling the message
$conversation = new Conversation(
$this->getMessage()->getChat()->getId(),
$this->getMessage()->getFrom()->getId()
);
//Fetch conversation command if it exists and execute it
if ($conversation->exists() && ($command = $conversation->getCommand())) {
return $this->telegram->executeCommand($command, $this->update);
}
}*/
return Request::emptyResponse();
}
}
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Conversation;
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
use Longman\TelegramBot\Request;
/**
* User "/cancel" command
*
* This command cancels the currently active conversation and
* returns a message to let the user know which conversation it was.
* If no conversation is active, the returned message says so.
*/
class CancelCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'cancel';
protected $description = 'Cancel the currently active conversation';
protected $usage = '/cancel';
protected $version = '0.1.1';
protected $need_mysql = true;
/**#@-*/
/**
* {@inheritdoc}
*/
public function execute()
{
$text = 'No active conversation!';
//Cancel current conversation if any
$conversation = new Conversation(
$this->getMessage()->getFrom()->getId(),
$this->getMessage()->getChat()->getId()
);
if ($conversation_command = $conversation->getCommand()) {
$conversation->cancel();
$text = 'Conversation "' . $conversation_command . '" cancelled!';
}
return $this->hideKeyboard($text);
}
/**
* {@inheritdoc}
*/
public function executeNoDB()
{
return $this->hideKeyboard();
}
/**
* Hide the keyboard and output a text
*
* @param string $text
*
* @return Entities\ServerResponse
*/
private function hideKeyboard($text = '')
{
return Request::sendMessage([
'reply_markup' => new ReplyKeyboardHide(['selective' => true]),
'chat_id' => $this->getMessage()->getChat()->getId(),
'text' => $text,
]);
}
}
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Commands\UserCommands;
use Longman\TelegramBot\Request;
use Longman\TelegramBot\Conversation;
use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Entities\ForceReply;
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
/**
* User "/survery" command
*/
class SurveyCommand extends UserCommand
{
/**#@+
* {@inheritdoc}
*/
protected $name = 'survey';
protected $description = 'Survery for bot users';
protected $usage = '/survey';
protected $version = '0.1.1';
protected $need_mysql = true;
/**#@-*/
/**
* Conversation Object
*
* @var Longman\TelegramBot\Conversation
*/
protected $conversation;
/**
* {@inheritdoc}
*/
public function execute()
{
$message = $this->getMessage();
$chat = $message->getChat();
$user = $message->getFrom();
$text = $message->getText(true);
$chat_id = $chat->getId();
$user_id = $user->getId();
//Preparing Respose
$data = [];
if ($chat->isGroupChat() || $chat->isSuperGroup()) {
//reply to message id is applied by default
$data['reply_to_message_id'] = $message_id;
//Force reply is applied by default to so can work with privacy on
$data['reply_markup'] = new ForceReply([ 'selective' => true]);
}
$data['chat_id'] = $chat_id;
//Conversation start
$this->conversation = new Conversation($user_id, $chat_id, $this->getName());
//cache data from the tracking session if any
if (!isset($this->conversation->notes['state'])) {
$state = '0';
} else {
$state = $this->conversation->notes['state'];
}
//state machine
//entrypoint of the machine state if given by the track
//Every time the step is achived the track is updated
switch ($state) {
case 0:
if (empty($text)) {
$this->conversation->notes['state'] = 0;
$this->conversation->update();
$data['text'] = 'Type your name:';
$data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['name'] = $text;
$text = '';
// no break
case 1:
if (empty($text)) {
$this->conversation->notes['state'] = 1;
$this->conversation->update();
$data['text'] = 'Type your surname:';
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['surname'] = $text;
++$state;
$text = '';
// no break
case 2:
if (empty($text) || !is_numeric($text)) {
$this->conversation->notes['state'] = 2;
$this->conversation->update();
$data['text'] = 'Type your age:';
if (!empty($text) && !is_numeric($text)) {
$data['text'] = 'Type your age, must be a number';
}
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['age'] = $text;
$text = '';
// no break
case 3:
if (empty($text) || !($text == 'M' || $text == 'F')) {
$this->conversation->notes['state'] = 3;
$this->conversation->update();
$keyboard = [['M','F']];
$reply_keyboard_markup = new ReplyKeyboardMarkup(
[
'keyboard' => $keyboard ,
'resize_keyboard' => true,
'one_time_keyboard' => true,
'selective' => true
]
);
$data['reply_markup'] = $reply_keyboard_markup;
$data['text'] = 'Select your gender:';
if (!empty($text) && !($text == 'M' || $text == 'F')) {
$data['text'] = 'Select your gender, choose a keyboard option:';
}
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['gender'] = $text;
$text = '';
// no break
case 4:
if (is_null($message->getLocation())) {
$this->conversation->notes['state'] = 4;
$this->conversation->update();
$data['text'] = 'Insert your home location (need location object):';
$data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['longitude'] = $message->getLocation()->getLongitude();
$this->conversation->notes['latitude'] = $message->getLocation()->getLatitude();
// no break
case 5:
if (is_null($message->getPhoto())) {
$this->conversation->notes['state'] = 5;
$this->conversation->update();
$data['text'] = 'Insert your picture:';
$result = Request::sendMessage($data);
break;
}
$this->conversation->notes['photo_id'] = $message->getPhoto()[0]->getFileId();
// no break
case 6:
$out_text = '/Survey result:' . "\n";
unset($this->conversation->notes['state']);
foreach ($this->conversation->notes as $k => $v) {
$out_text .= "\n" . ucfirst($k).': ' . $v;
}
$data['photo'] = $this->conversation->notes['photo_id'];
$data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
$data['caption'] = $out_text;
$this->conversation->stop();
$result = Request::sendPhoto($data);
break;
}
return $result;
}
}
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot;
/**
* Class Conversation
*
* Only one conversation can be active at any one time.
* A conversation is directly linked to a user, chat and the command that is managing the conversation.
*/
class Conversation
{
/**
* All information fetched from the database
*
* @var array
*/
protected $conversation = null;
/**
* Notes stored inside the conversation
*
* @var array
*/
protected $protected_notes = null;
/**
* Notes to be stored
*
* @var array
*/
public $notes = null;
/**
* Telegram user id
*
* @var int
*/
protected $user_id;
/**
* Telegram chat id
*
* @var int
*/
protected $chat_id;
/**
* Command to be executed if the conversation is active
*
* @var string
*/
protected $command;
/**
* Conversation contructor to initialize a new conversation
*
* @param int $user_id
* @param int $chat_id
* @param string $command
*/
public function __construct($user_id, $chat_id, $command = null)
{
$this->user_id = $user_id;
$this->chat_id = $chat_id;
$this->command = $command;
//Try to load an existing conversation if possible
if (!$this->load() && $command !== null) {
//A new conversation start
$this->start();
}
}
/**
* Clear all conversation variables.
*
* @return bool Always return true, to allow this method in an if statement.
*/
protected function clear()
{
$this->conversation = null;
$this->protected_notes = null;
$this->notes = null;
return true;
}
/**
* Load the conversation from the database
*
* @return bool
*/
protected function load()
{
//Select an active conversation
$conversation = ConversationDB::selectConversation($this->user_id, $this->chat_id, 1);
if (isset($conversation[0])) {
//Pick only the first element
$this->conversation = $conversation[0];
//Load the command from the conversation if it hasn't been passed
$this->command = $this->command ?: $this->conversation['command'];
if ($this->command !== $this->conversation['command']) {
$this->cancel();
return false;
}
//Load the conversation notes
$this->protected_notes = json_decode($this->conversation['notes'], true);
$this->notes = $this->protected_notes;
}
return $this->exists();
}
/**
* Check if the conversation already exists
*
* @return bool
*/
public function exists()
{
return ($this->conversation !== null);
}
/**
* Start a new conversation if the current command doesn't have one yet
*
* @return bool
*/
protected function start()
{
if (!$this->exists() && $this->command) {
if (ConversationDB::insertConversation(
$this->user_id,
$this->chat_id,
$this->command
)) {
return $this->load();
}
}
return false;
}
/**
* Delete the current conversation
*
* Currently the Conversation is not deleted but just set to 'stopped'
*
* @return bool
*/
public function stop()
{
return ($this->updateStatus('stopped') && $this->clear());
}
/**
* Cancel the current conversation
*
* @return bool
*/
public function cancel()
{
return ($this->updateStatus('cancelled') && $this->clear());
}
/**
* Update the status of the current conversation
*
* @param string $status
*
* @return bool
*/
protected function updateStatus($status)
{
if ($this->exists()) {
$fields = ['status' => $status];
$where = [
'id' => $this->conversation['id'],
'status' => 'active',
'user_id' => $this->user_id,
'chat_id' => $this->chat_id,
];
if (ConversationDB::updateConversation($fields, $where)) {
return true;
}
}
return false;
}
/**
* Store the array/variable in the database with json_encode() function
*
* @return bool
*/
public function update()
{
if ($this->exists()) {
$fields = ['notes' => json_encode($this->notes)];
//I can update a conversation whatever the state is
$where = ['id' => $this->conversation['id']];
if (ConversationDB::updateConversation($fields, $where)) {
return true;
}
}
return false;
}
/**
* Retrieve the command to execute from the conversation
*
* @return string|null
*/
public function getCommand()
{
return $this->command;
}
}
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot;
use Longman\TelegramBot\DB;
use Longman\TelegramBot\Exception\TelegramException;
/**
* Class ConversationDB
*/
class ConversationDB extends DB
{
/**
* Initilize conversation table
*/
public static function initializeConversation()
{
if (!defined('TB_CONVERSATION')) {
define('TB_CONVERSATION', self::$table_prefix . 'conversation');
}
}
/**
* Select a conversation from the DB
*
* @param int $user_id
* @param int $chat_id
* @param bool $limit
*
* @return array|bool
*/
public static function selectConversation($user_id, $chat_id, $limit = null)
{
if (!self::isDbConnected()) {
return false;
}
try {
$query = 'SELECT * FROM `' . TB_CONVERSATION . '` ';
$query .= 'WHERE `status` = :status ';
$query .= 'AND `chat_id` = :chat_id ';
$query .= 'AND `user_id` = :user_id ';
$tokens = [':chat_id' => $chat_id, ':user_id' => $user_id];
if (!is_null($limit)) {
$query .= ' LIMIT :limit';
}
$sth = self::$pdo->prepare($query);
$active = 'active';
$sth->bindParam(':status', $active, \PDO::PARAM_STR);
$sth->bindParam(':user_id', $user_id, \PDO::PARAM_INT);
$sth->bindParam(':chat_id', $chat_id, \PDO::PARAM_INT);
$sth->bindParam(':limit', $limit, \PDO::PARAM_INT);
$sth->execute();
$results = $sth->fetchAll(\PDO::FETCH_ASSOC);
} catch (\Exception $e) {
throw new TelegramException($e->getMessage());
}
return $results;
}
/**
* Insert the conversation in the database
*
* @param int $user_id
* @param int $chat_id
* @param string $command
*
* @return bool
*/
public static function insertConversation($user_id, $chat_id, $command)
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('INSERT INTO `' . TB_CONVERSATION . '`
(
`status`, `user_id`, `chat_id`, `command`, `notes`, `created_at`, `updated_at`
)
VALUES (
:status, :user_id, :chat_id, :command, :notes, :date, :date
)
');
$active = 'active';
//$notes = json_encode('');
$notes = '""';
$created_at = self::getTimestamp();
$sth->bindParam(':status', $active);
$sth->bindParam(':command', $command);
$sth->bindParam(':user_id', $user_id);
$sth->bindParam(':chat_id', $chat_id);
$sth->bindParam(':notes', $notes);
$sth->bindParam(':date', $created_at);
$status = $sth->execute();
} catch (\Exception $e) {
throw new TelegramException($e->getMessage());
}
return $status;
}
/**
* Update a specific conversation
*
* @param array $fields_values
* @param array $where_fields_values
*
* @return bool
*/
public static function updateConversation(array $fields_values, array $where_fields_values)
{
return self::update(TB_CONVERSATION, $fields_values, $where_fields_values);
}
/**
* Update the conversation in the database
*
* @param string $table
* @param array $fields_values
* @param array $where_fields_values
*
* @todo This function is generic should be moved in DB.php
*
* @return bool
*/
public static function update($table, array $fields_values, array $where_fields_values)
{
if (!self::isDbConnected()) {
return false;
}
//Auto update the field update_at
$fields_values['updated_at'] = self::getTimestamp();
//Values
$update = '';
$tokens = [];
$tokens_counter = 0;
$a = 0;
foreach ($fields_values as $field => $value) {
if ($a) {
$update .= ', ';
}
++$a;
++$tokens_counter;
$update .= '`' . $field . '` = :' . $tokens_counter;
$tokens[':' . $tokens_counter] = $value;
}
//Where
$a = 0;
$where = '';
foreach ($where_fields_values as $field => $value) {
if ($a) {
$where .= ' AND ';
} else {
++$a;
$where .= 'WHERE ';
}
++$tokens_counter;
$where .= '`' . $field .'`= :' . $tokens_counter ;
$tokens[':' . $tokens_counter] = $value;
}
$query = 'UPDATE `' . $table . '` SET ' . $update . ' ' . $where;
try {
$sth = self::$pdo->prepare($query);
$status = $sth->execute($tokens);
} catch (\Exception $e) {
throw new TelegramException($e->getMessage());
}
return $status;
}
}
......@@ -30,7 +30,7 @@ class Telegram
*
* @var string
*/
protected $version = '0.28.0';
protected $version = '0.29.0';
/**
* Telegram API key
......@@ -177,6 +177,7 @@ class Telegram
public function enableMySQL(array $credential, $table_prefix = null)
{
$this->pdo = DB::initialize($credential, $this, $table_prefix);
ConversationDB::initializeConversation();
$this->mysql_enabled = true;
return $this;
}
......
CREATE TABLE IF NOT EXISTS `user` (
`id` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier',
`id` bigint COMMENT 'Unique user identifier',
`first_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User first name',
`last_name` CHAR(255) DEFAULT NULL COMMENT 'User last name',
`username` CHAR(255) DEFAULT NULL COMMENT 'User username',
......@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS `user` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `chat` (
`id` bigint NULL DEFAULT NULL COMMENT 'Unique user or chat identifier',
`id` bigint COMMENT 'Unique user or chat identifier',
`type` ENUM('private', 'group', 'supergroup', 'channel') NOT NULL COMMENT 'chat type private, group, supergroup or channel',
`title` CHAR(255) DEFAULT '' COMMENT 'chat title null if case of single chat with the bot',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
......@@ -20,8 +20,8 @@ CREATE TABLE IF NOT EXISTS `chat` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `user_chat` (
`user_id` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier',
`chat_id` bigint NULL DEFAULT NULL COMMENT 'Unique user or chat identifier',
`user_id` bigint COMMENT 'Unique user identifier',
`chat_id` bigint COMMENT 'Unique user or chat identifier',
PRIMARY KEY (`user_id`, `chat_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE,
......@@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS `user_chat` (
CREATE TABLE IF NOT EXISTS `inline_query` (
`id` bigint UNSIGNED NULL COMMENT 'Unique identifier for this query.',
`id` bigint UNSIGNED COMMENT 'Unique identifier for this query.',
`user_id` bigint NULL COMMENT 'Sender',
`query` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Text of the query',
`offset` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Offset of the result',
......@@ -41,11 +41,11 @@ CREATE TABLE IF NOT EXISTS `inline_query` (
FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `chosen_inline_query` (
`id` bigint UNSIGNED NULL AUTO_INCREMENT COMMENT 'Unique identifier for chosen query.',
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for chosen query.',
`result_id` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Id of the chosen result',
`user_id` bigint NULL COMMENT 'Sender',
`query` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Text of the query',
......@@ -55,11 +55,11 @@ CREATE TABLE IF NOT EXISTS `chosen_inline_query` (
FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `message` (
`id` bigint UNSIGNED NULL COMMENT 'Unique message identifier',
`id` bigint UNSIGNED COMMENT 'Unique message identifier',
`user_id` bigint NULL COMMENT 'User identifier',
`chat_id` bigint NULL DEFAULT NULL COMMENT 'Chat identifier.',
`date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was sent in timestamp format',
......@@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS `message` (
CREATE TABLE IF NOT EXISTS `telegram_update` (
`id` bigint UNSIGNED NULL COMMENT 'The update\'s unique identifier.',
`id` bigint UNSIGNED COMMENT 'The update\'s unique identifier.',
`message_id` bigint UNSIGNED DEFAULT NULL COMMENT 'Unique message identifier',
`inline_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'The query unique identifier.',
`chosen_inline_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'The chosen query unique identifier.',
......@@ -135,4 +135,23 @@ CREATE TABLE IF NOT EXISTS `telegram_update` (
REFERENCES `chosen_inline_query` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `conversation` (
`id` bigint(20) unsigned AUTO_INCREMENT COMMENT 'Row unique id',
`user_id` bigint NULL DEFAULT NULL COMMENT 'User id',
`chat_id` bigint NULL DEFAULT NULL COMMENT 'Telegram chat_id can be a the user id or the chat id ',
`status` ENUM('active', 'cancelled', 'stopped') NOT NULL DEFAULT 'active' COMMENT 'active conversation is active, cancelled conversation has been truncated before end, stopped conversation has end',
`command` varchar(160) DEFAULT '' COMMENT 'Default Command to execute',
`notes` varchar(1000) DEFAULT 'NULL' COMMENT 'Data stored from command',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `chat_id` (`chat_id`),
KEY `status` (`status`),
FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`),
FOREIGN KEY (`chat_id`)
REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
......@@ -10,7 +10,11 @@
namespace Tests;
use Longman\TelegramBot\DB;
use Longman\TelegramBot\Entities\Chat;
use Longman\TelegramBot\Entities\Message;
use Longman\TelegramBot\Entities\Update;
use Longman\TelegramBot\Entities\User;
/**
* @package TelegramTest
......@@ -21,6 +25,31 @@ use Longman\TelegramBot\Entities\Update;
*/
class TestHelpers
{
/**
* Data template of a user.
*
* @var array
*/
protected static $user_template = [
'id' => 1,
'first_name' => 'first',
'last_name' => 'last',
'username' => 'user',
];
/**
* Data template of a chat.
*
* @var array
*/
protected static $chat_template = [
'id' => 1,
'first_name' => 'first',
'last_name' => 'last',
'username' => 'name',
'type' => 'private',
];
/**
* Set the value of a private/protected property of an object
*
......@@ -50,7 +79,7 @@ class TestHelpers
'message' => [
'message_id' => 1,
'chat' => [
'id' => 1,
'id' => 1,
],
'date' => 1,
]
......@@ -71,23 +100,102 @@ class TestHelpers
'update_id' => 1,
'message' => [
'message_id' => 1,
'from' => [
'id' => 1,
'first_name' => 'first',
'last_name' => 'last',
'username' => 'user',
],
'chat' => [
'id' => 1,
'first_name' => 'first',
'last_name' => 'last',
'username' => 'name',
'type' => 'private',
],
'date' => 1,
'text' => $command_text,
'from' => self::$user_template,
'chat' => self::$chat_template,
'date' => 1,
'text' => $command_text,
],
];
return self::getFakeUpdateObject($data);
}
/**
* Return a fake user object.
*
* @return Entities\User
*/
public static function getFakeUserObject()
{
return new User(self::$user_template);
}
/**
* Return a fake chat object.
*
* @return Entities\Chat
*/
public static function getFakeChatObject()
{
return new Chat(self::$chat_template);
}
/**
* Return a fake message object using the passed ids.
*
* @param integer $message_id
* @param integer $user_id
* @param integer $chat_id
*
* @return Entities\Message
*/
public static function getFakeMessageObject($message_id = 1, $user_id = 1, $chat_id = 1)
{
return new Message([
'message_id' => $message_id,
'from' => ['id' => $user_id] + self::$user_template,
'chat' => ['id' => $chat_id] + self::$chat_template,
'date' => 1,
], 'botname');
}
/**
* Start a fake conversation for the passed command and return the randomly generated ids.
*
* @param string $command
* @return array
*/
public static function startFakeConversation($command)
{
if (!DB::isDbConnected()) {
return false;
}
//Just get some random values.
$message_id = rand();
$user_id = rand();
$chat_id = rand();
//Make sure we have a valid user and chat available.
$message = self::getFakeMessageObject($message_id, $user_id, $chat_id);
DB::insertMessageRequest($message);
DB::insertUser($message->getFrom(), null, $message->getChat());
return compact('message_id', 'user_id', 'chat_id');
}
/**
* Empty all tables for the passed database
*
* @param array $credentials
*/
public static function emptyDB(array $credentials)
{
$dsn = 'mysql:host=' . $credentials['host'] . ';dbname=' . $credentials['database'];
$options = [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'];
try {
$pdo = new \PDO($dsn, $credentials['user'], $credentials['password'], $options);
$pdo->prepare('
DELETE FROM `conversation`;
DELETE FROM `telegram_update`;
DELETE FROM `chosen_inline_query`;
DELETE FROM `inline_query`;
DELETE FROM `message`;
DELETE FROM `user_chat`;
DELETE FROM `chat`;
DELETE FROM `user`;
')->execute();
} catch (\Exception $e) {
throw new TelegramException($e->getMessage());
}
}
}
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Tests\Unit;
use Tests\TestHelpers;
use Longman\TelegramBot\Conversation;
use Longman\TelegramBot\Telegram;
/**
* @package TelegramTest
* @author Avtandil Kikabidze <akalongman@gmail.com>
* @copyright Avtandil Kikabidze <akalongman@gmail.com>
* @license http://opensource.org/licenses/mit-license.php The MIT License (MIT)
* @link http://www.github.com/akalongman/php-telegram-bot
*/
class ConversationTest extends TestCase
{
/**
* @var \Longman\TelegramBot\Telegram
*/
private $telegram;
/**
* setUp
*/
protected function setUp()
{
$credentials = [
'host' => '127.0.0.1',
'user' => 'travis',
'password' => '',
'database' => 'telegrambot',
];
$this->telegram = new Telegram('testapikey', 'testbotname');
$this->telegram->enableMySQL($credentials);
//Make sure we start with an empty DB for each test.
TestHelpers::emptyDB($credentials);
}
/**
* @test
*/
public function conversationThatDoesntExistPropertiesSetCorrectly()
{
$conversation = new Conversation(123, 456);
$this->assertAttributeEquals(123, 'user_id', $conversation);
$this->assertAttributeEquals(456, 'chat_id', $conversation);
$this->assertAttributeEquals(null, 'command', $conversation);
}
/**
* @test
*/
public function conversationThatExistsPropertiesSetCorrectly()
{
$info = TestHelpers::startFakeConversation('command');
$conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
$this->assertAttributeEquals($info['user_id'], 'user_id', $conversation);
$this->assertAttributeEquals($info['chat_id'], 'chat_id', $conversation);
$this->assertAttributeEquals('command', 'command', $conversation);
}
/**
* @test
*/
public function conversationThatDoesntExistWithoutCommand()
{
$conversation = new Conversation(1, 1);
$this->assertFalse($conversation->exists());
$this->assertNull($conversation->getCommand());
}
/**
* @test
* @expectedException \Longman\TelegramBot\Exception\TelegramException
*/
public function conversationThatDoesntExistWithCommand()
{
new Conversation(1, 1, 'command');
}
/**
* @test
*/
public function newConversationThatWontExistWithoutCommand()
{
TestHelpers::startFakeConversation(null);
$conversation = new Conversation(0, 0);
$this->assertFalse($conversation->exists());
$this->assertNull($conversation->getCommand());
}
/**
* @test
*/
public function newConversationThatWillExistWithCommand()
{
$info = TestHelpers::startFakeConversation('command');
$conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
$this->assertTrue($conversation->exists());
$this->assertEquals('command', $conversation->getCommand());
}
/**
* @test
*/
public function stopConversation()
{
$info = TestHelpers::startFakeConversation('command');
$conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
$this->assertTrue($conversation->exists());
$conversation->stop();
$conversation2 = new Conversation($info['user_id'], $info['chat_id']);
$this->assertFalse($conversation2->exists());
}
/**
* @test
*/
public function cancelConversation()
{
$info = TestHelpers::startFakeConversation('command');
$conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
$this->assertTrue($conversation->exists());
$conversation->cancel();
$conversation2 = new Conversation($info['user_id'], $info['chat_id']);
$this->assertFalse($conversation2->exists());
}
/**
* @test
*/
public function updateConversationNotes()
{
$info = TestHelpers::startFakeConversation('command');
$conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
$conversation->notes = 'newnote';
$conversation->update();
$conversation2 = new Conversation($info['user_id'], $info['chat_id'], 'command');
$this->assertSame('newnote', $conversation2->notes);
$conversation3 = new Conversation($info['user_id'], $info['chat_id']);
$this->assertSame('newnote', $conversation3->notes);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment