BotMan - Conversations method is not replying - php

i am working on facebook messenger bot. I am using Botman (botman.io) without Laravel or botman studio. Version of PHP is 7.4.
Simple hears and reply method works fine, but conversation replying method does not working.
If I try type hi|hello or some greetings, chatbot answer me "Hello! What is your firstname?", then I write my name and chatbot does not returns any text :-/
Can you help me where is a bug?
There is a conversation class:
namespace LiborMatejka\Conversations;
use BotMan\BotMan\Messages\Conversations\Conversation;
use BotMan\BotMan\Messages\Incoming\Answer;
class OnboardingConversation extends Conversation {
protected $firstname;
protected $email;
function askFirstname() {
$this->ask('Hello! What is your firstname?', function (Answer $answer) {
// Save result
$this->firstname = $answer->getText();
$this->say('Nice to meet you ' . $this->firstname);
$this->askEmail();
});
}
public function askEmail() {
$this->ask('One more thing - what is your email?', function (Answer $answer) {
// Save result
$this->email = $answer->getText();
$this->say('Great - that is all we need, ' . $this->firstname);
});
//$this->bot->typesAndWaits(2);
}
public function run() {
// This will be called immediately
$this->askFirstname();
}
}
and there is config:
require_once "vendor/autoload.php";
require_once "class/onboardingConversation.php";
use BotMan\BotMan\BotMan;
use BotMan\BotMan\BotManFactory;
use BotMan\BotMan\Drivers\DriverManager;
use BotMan\Drivers\Facebook\FacebookDriver;
use LiborMatejka\Conversations\OnboardingConversation;
$config = [
// Your driver-specific configuration
'facebook' => [
'token' => 'my_token',
'app_secret' => 'my_secret_app_code',
'verification' => 'verification_code',
],
'botman' => [
'conversation_cache_time' => 0,
],
];
// Load the driver(s) you want to use
DriverManager::loadDriver(\BotMan\Drivers\Facebook\FacebookDriver::class);
// Create an instance
$botman = BotManFactory::create($config);
$botman->hears('ahoj|hi|hello|cau|cus|zdar|zdarec|cago|hey|ciao', function (BotMan $bot) {
$bot->startConversation(new OnboardingConversation);
});
// Start listening
$botman->listen();

Add symfony/cache to your project using composer
composer require symfony/cache
Put following at top of index.php (or other) file where you're setting up BotMan
use BotMan\BotMan\Cache\SymfonyCache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
Then create your BotMan using the following:
$adapter = new FilesystemAdapter();
$botman = BotManFactory::create($config, new SymfonyCache($adapter));
Then use your $botman variable accordingly, like example below:
$botman->hears('Hi', function (BotMan $bot) {
$bot->typesAndWaits(2);
$bot->reply('Hello and welcome');
$bot->typesAndWaits(2);
$bot->ask('Anything I can do for you today?',function($answer, $bot){
$bot->say("Oh, really! You said '{$answer->getText()}'... is that right?");
});
});

I would rather to use auto-wiring to inject the SymfonyCache anywhere you create the Botman instance, without creating the adapter and cache again and again.
Step 1: config the cache in cache.yaml
framework:
cache:
# botman: cache adapter
app: cache.adapter.filesystem
Step 2: autowiring in services.yaml
services:
BotMan\BotMan\Cache\SymfonyCache:
arguments:
$adapter: '#cache.app'
Step 3: Inject the SymfonyCache where you need, for example in ChatController::message()
public function message(SymfonyCache $symfonyCache): Response
{
....
$botman = BotManFactory::create([], $symfonyCache);
....
$botman->hears(
'survey',
function (BotMan $bot) {
$bot->startConversation(new OnBoardingConversation());
}
);
}
To create the OnBoardingConversation, just follow the documentation on create a conversation in botman

Related

Laravel 6 - Pass a parameter to $app->when()->needs()->give()

I am trying to use this package to push notifications to users via OneSignal. However I needed to make a little change. My API serves two (related) apps and I have two OneSignal configs. I am trying to override its ServiceProvider (using this technique).
The ServiceProvider presents itself as follows
<?php
namespace NotificationChannels\OneSignal;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Support\ServiceProvider;
use NotificationChannels\OneSignal\Exceptions\InvalidConfiguration;
class OneSignalServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*/
public function boot()
{
$this->app->when(OneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
}
The behavior that I want to change is located in the line
$oneSignalConfig = config('services.onesignal');
As it assumes that my config/services.php has the following entry (stated in the doc) :
// config/services.php
...
'onesignal' => [
'app_id' => env('ONESIGNAL_APP_ID'),
'rest_api_key' => env('ONESIGNAL_REST_API_KEY')
],
...
Whereas I want to set my config/services.php as follows
// config/services.php
...
'onesignal' => [
'app1' => [
'app_id' => env('ONESIGNAL_1_APP_ID'),
'rest_api_key' => env('ONESIGNAL_1_REST_API_KEY')
],
'app2' => [
'app_id' => env('ONESIGNAL_2_APP_ID'),
'rest_api_key' => env('ONESIGNAL_2_REST_API_KEY')
],
],
...
And I want somehow to tell my ServiceProvider (through some kind of parameter) to either do
$oneSignalConfig = config('services.onesignal.app1');
OR
$oneSignalConfig = config('services.onesignal.app2');
But I didn't find any way to pass a parameter to the class, the boot function or the give method (and if I understood well I shouldn't even be doing that).
The only way I could think of is to create two classes that extend the OneSignalChannel::class
and duplicate code in the boot function so it becomes as follows :
public function boot()
{
$this->app->when(FirstOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app1');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
$this->app->when(SecondOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app2');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
The difference in the when provoking a difference in the config but it seems a lot of duplication and not extensible (what if I had three apps).
Should I use this method, or is there a way to pass a parameter to this ServiceProvider or is there another solution ?
https://stackoverflow.com/a/34224082/10371024
I could see what you need, but to pass parameter to boot method is not a good idea according to Laravel architecture. You may try to get what you want with using events as Vladislav suggested.

Access same url with different Controller and method for API

I am currently working on API in laravel 5.6 and I would like versioning for APIs like v1 and v2.
my problem is I want to run one URL and access both API version, I am passing version number into HEADER and access API controller according to the version number. I am also using middle ware to check version number but not getting what I need.
Here is my web.php
Route::post('/api/getticktes/{id}', 'Api\v1\TicketController#show')->middleware('checkHeaderV1');
Header
version :- v1
version :- v2
My controller directory is
Controller
-Api
--v1
---TicketController.php
--v2
---TicketController.php
You can use this class, i have used it in my old project, may be it is not the efficient way but it will work
ApiVersion class
namespace App\Http;
use Illuminate\Http\Request;
class ApiVersion
{
protected static $valid_api_versions = [
1 => 'v1',
2 => 'v2'
];
protected static function get($request)
{
$allApiVersions = array_keys(self::$valid_api_versions);
$latestVersion = $allApiVersions[count($allApiVersions) - 1];
$apiVersion = $request->header('api-version', $latestVersion);
return in_array($apiVersion, $allApiVersions) ? $apiVersion : $latestVersion;
}
protected static function getNamespace($apiVersion)
{
return 'Api\\' . self::$valid_api_versions[$apiVersion];
}
public static function versionNamespace()
{
$request = Request::capture();
return $apiNamespace = ApiVersion::getNamespace(self::get($request));
}
}
Now use this middleware in api routes file for namespace
//API routes
$versionNameSpace = ApiVersion::versionNamespace();
Route::group(['middleware' => ['api'], 'namespace' => "{$versionNameSpace}"], function () {
});
Add api-version in your request header. The value should be 1 or 2 etc

Codeigniter - Botman - reply method does not work

I'm using Botman 2.0 and Codeigniter 3.1.6
My owner server with HTTPS & FB Webhook setup successfully.
How do I have to setup my project with codeigniter? Because, I set the FB Webhook setup successfully, but, when I type "hi" on messenger this not reply.
Could you give me some advice for that my chat bot works correctly.
Note 1: I had test with PHP (Without framework) and This works correctly.
Note 2: I had test with NodeJS and This works correctly.
use BotMan\BotMan\BotMan;
use BotMan\BotMan\BotManFactory;
use BotMan\BotMan\Drivers\DriverManager;
class Facebook_messenger extends CI_Controller {
function __construct()
{
parent::__construct();
}
public function bot(){
$config = [
// Your driver-specific configuration
'facebook' => [
'token' => 'EAAZA1UK2sl8UBAIueLY2ZCHraasS3E52AS37wUVypMLQatW5taE71LByyl3nWNau8uTKYs1aw11lQXpDOfPrrQE8BLWf6PZBkwuQIdzea7lMeZCc4jCiZCqhKZABKnc2V8FNabVbHF9J6opkb6MCsBAxnqO0kDsgeoYV88gNOIJuTZAr9T7pzoBAZC',
//'app_secret' => 'f1a032fb00484a59c0322617b7623745a',
'verification'=>'xxxxapp',
]
];
// Load the driver(s) you want to use
DriverManager::loadDriver(\BotMan\Drivers\Facebook\FacebookDriver::class);
// Create an instance
$botman = BotManFactory::create($config);
// Give the bot something to listen for.
$botman->hears('hi', function (BotMan $bot) {
$bot->reply('Hello');
});
$botman->hears('delayed', function (BotMan $bot) {
$bot->reply('Field will remain in yellow (delayed)unless a game is scheduled on it.');
});
$botman->hears('canceled', function (BotMan $bot) {
$bot->reply('Games cancelled due to poor field conditions.');
});
// Start listening
$botman->listen();
}
}

How to use a caching system (memcached, redis or any other) with slim 3

I browsed the internet and didn't find much information on how to use any caching library with Slim framework 3.
Can anyone help me with this issue?
I use symfony/cache with Slim 3. You can use any other cache library, but I give an example setup for this specific library. And I should mention, this is actually independent of Slim or any other framework.
First you need to include this library in your project, I recommend using composer. I also will iinclude predis/predis to be able to use Redis adapter:
composer require symfony/cache predis/predis
Then I'll use Dependency Injection Container to setup cache pool to make it available to other objects which need to use caching features:
// If you created your project using slim skeleton app
// this should probably be placed in depndencies.php
$container['cache'] = function ($c) {
$config = [
'schema' => 'tcp',
'host' => 'localhost',
'port' => 6379,
// other options
];
$connection = new Predis\Client($config);
return new Symfony\Component\Cache\Adapter\RedisAdapter($connection);
}
Now you have a cache item pool in $container['cache'] which has methods defined in PSR-6.
Here is a sample code using it:
class SampleClass {
protected $cache;
public function __construct($cache) {
$this->cache = $cache;
}
public function doSomething() {
$item = $this->cache->getItem('unique-cache-key');
if ($item->isHit()) {
return 'I was previously called at ' . $item->get();
}
else {
$item->set(time());
$item->expiresAfter(3600);
$this->cache->save($item);
return 'I am being called for the first time, I will return results from cache for the next 3600 seconds.';
}
}
}
Now when you want to create new instance of SampleClass you should pass this cache item pool from the DIC, for example in a route callback:
$app->get('/foo', function (){
$bar = new SampleClass($this->get('cache'));
return $bar->doSomething();
});
$memcached = new \Memcached();
$memcached->addServer($cachedHost, $cachedPort);
$metadataCache = new \Doctrine\Common\Cache\MemcachedCache();
$metadataCache->setMemcached($memcached);
$queryCache = new \Doctrine\Common\Cache\MemcachedCache();
$queryCache->setMemcached($memcached);

How can I use Doctrine 2's console tools with dynamically generated EntityManagers?

We are using Doctrine 2 in our app, but due to our infrastructure, we do not have a static configuration for database connections. Instead, we have a collection of singletons in a service provider for each database we need to connect to, and we select a random database host for then when we connect.
Unfortunately, we are seeing some performance degradation in Doctrine's getRepository() function. I believe the issue is that Doctrine needs to generate its proxy classes at runtime (even in production) because we cannot figure out how to configure the CLI tools in order to create them at build time.
We are using the Laravel framework for the application.
Here's an example of our Laravel service provider which makes the repositories available for dependency injection.
<?php
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
use App\Database\Entities;
use App\Database\Repositories;
use App\Database\Constants\EntityConstants;
class DoctrineServiceProvider extends ServiceProvider
{
// Create a singleton for the Doctrine Manager. This class will handle entity manager generation.
$this->app->singleton(DoctrineManager::class, function ($app)
{
return new DoctrineManager(
$app->make(ConnectionFactory::class),
[
EntityConstants::ENTITY_CLASS_DATABASE1 => [app_path('Database/Entities/Database1')],
EntityConstants::ENTITY_CLASS_DATABASE2 => [app_path('Database/Entities/Database2')],
],
config('app.debug'),
$this->app->make(LoggerInterface::class)
);
});
// Register the first repository
$this->app->singleton(Repositories\Database1\RepositoryA1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA1::class);
});
// Register the second repository
$this->app->singleton(Repositories\Database1\RepositoryA2::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA2::class);
});
// Register a repository for the second database
$this->app->singleton(Repositories\Database2\RepositoryB1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE2)
->getRepository(Entities\Database2\RepositoryB1::class);
});
}
Here's the class that generates EntityManagers for Doctrine:
<?php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connections\MasterSlaveConnection;
use Proprietary\ConnectionFactory;
class Manager
{
private $c_factory;
private $paths;
private $connections = [];
private $entity_managers = [];
public function __construct(
ConnectionFactory $cf,
array $paths
)
{
$this->c_factory = $cf;
$this->paths = $paths;
}
public function getConnection($name, $partition = false, $region = false)
{
// Get a list of servers for this database and format them for use with Doctrine
$servers = self::formatServers($name, $this->c_factory->getServers($name, true, $partition, $region));
// Generate a connection for the entity manager using the servers we have.
$connection = DriverManager::getConnection(
array_merge([
'wrapperClass' => MasterSlaveConnection::class,
'driver' => 'pdo_mysql',
], $servers)
);
return $connection;
}
public function getEntityManager($name, $partition = false, $region = false)
{
// Should these things be cached somehow at build time?
$config = Setup::createAnnotationMetadataConfiguration($this->paths[$name], false);
$config->setAutoGenerateProxyClasses(true);
// Set up the connection
$connection = $this->getConnection($name, $partition, $region);
$entity_manager = EntityManager::create($connection, $config);
return $entity_manager;
}
// Converts servers from a format provided by our proprietary code to a format Doctrine can use.
private static function formatServers($db_name, array $servers)
{
$doctrine_servers = [
'slaves' => [],
];
foreach ($servers as $server)
{
// Format for Doctrine
$server = [
'user' => $server['username'],
'password' => $server['password'],
'host' => $server['hostname'],
'dbname' => $db_name,
'charset' => 'utf8',
];
// Masters can also be used as slaves.
$doctrine_servers['slaves'][] = $server;
// Servers are ordered by which is closest, and Doctrine only allows a
// single master, so if we already set one, don't overwrite it.
if ($server['is_master'] && !isset($doctrine_servers['master']))
{
$doctrine_servers['master'] = $server;
}
}
return $doctrine_servers;
}
}
Our service classes use dependency injection to get the repository singletons defined in the service provider. When we use the singletons for the first time, Doctrine will use the entity class defined in the service provider and get the connection associated with the repository.
Is there any way we can enable the CLI tools with this configuration? Are there any other ways that we can optimize this for use in production?
Thanks.
I was able to solve the problem thanks to a suggestion from the Doctrine IRC channel. Since the CLI tools can only handle a single database, I created a doctrine-cli directory containing a base-config.php file and a subdirectory for each of the databases we use.
Here's an example file structure:
doctrine-cli/
|- database1/
| |- cli-config.php
|- database2/
| |- cli-config.php
|- base-config.php
The base-config.php file looks like this:
<?php
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
require __DIR__ . '/../bootstrap/autoload.php';
class DoctrineCLIBaseConfig
{
private $helper_set;
public function __construct($entity_constant, $entity_namespace)
{
$app = require_once __DIR__ . '/../bootstrap/app.php';
// Proprietary factory for getting our databse details
$connection_factory = new ConnectionFactory(...);
// Our class that parses the results from above and handles our Doctrine connection.
$manager = new DoctrineManager(
$connection_factory,
[$entity_constant => [app_path('Database/Entities/' . $entity_namespace)]],
false,
null,
null
);
$em = $manager->getEntityManager($entity_constant);
$this->helper_set = new HelperSet([
'db' => new ConnectionHelper($em->getConnection()),
'em' => new EntityManagerHelper($em),
]);
}
public function getHelperSet()
{
return $this->helper_set;
}
}
Here's an example cli-config.php from the database directory:
<?php
use App\Database\Constants\EntityConstants;
require __DIR__ . "/../base-config.php";
$config = new DoctrineCLIBaseConfig(
EntityConstants::ENTITY_CLASS_DATABASE1,
"database1"
);
return $config->getHelperSet();
Now, I'm able to cycle through each of the directories and run commands like so:
php ../../vendor/bin/doctrine orm:generate-proxies
For our build process, I wrote a simple shell script that cycles through the directories and runs the orm:generate-proxies command.

Categories