Giter Site home page Giter Site logo

aricpp's People

Contributors

carlos-menezes avatar daniele77 avatar mredd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

aricpp's Issues

Can't add a Pause method to Recording (and how to handle recordings)

Hello.

First and foremost, I'm not sure if this is the correct way to control a recording. I create a new recording when a channel enters the Stasis application. Then, I want to be able to control said recording (i.e. pause it). I decided to add a new method to the Recording class in recording.hpp:

Proxy& Pause(std::string id)
        {
            return Proxy::Command(Method::post, "/ari/recordings/live/" + id + "/pause", client);
        }

This is the code for the Stasis application:

#include <iostream>

#include <boost/asio/io_service.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

#include <aricpp/client.h>
#include <aricpp/jsontree.h>
#include <aricpp/arimodel.h>
#include <aricpp/channel.h>


int main()
{
	boost::asio::io_service ios;
	aricpp::Client client(ios, "localhost", "8088", "asterisk", "asterisk", "Announcement");
	aricpp::AriModel model(client);
	
	std::unique_ptr<aricpp::Bridge> bridge;

	aricpp::Recording recordingControl = aricpp::Recording(); // Used to control a recording
	std::string recordingId;

	client.Connect([&](boost::system::error_code e) {
		if (e) std::cerr << "Error connecting: " << e.message() << '\n';
		else std::cout << "Connected to Asterisk ARI" << "\n";

		model.CreateBridge(
			[&bridge](std::unique_ptr<aricpp::Bridge> newBridge)
			{
				bridge = move(newBridge);
				std::cout << "Bridge created" << std::endl;
			},
			aricpp::Bridge::Type::mixing);

		model.OnStasisStarted([&](std::shared_ptr<aricpp::Channel> channel, bool external) {
			std::string channelName = (std::string)channel->Name();

			bridge->Add(*channel, false, aricpp::Bridge::Role::participant);
			std::cout << "CHANNEL JOINED AND ADDED TO BRIDGE: " << channelName << std::endl;

			if (!channelName.rfind("Recorder", 0) == 0) {
				boost::uuids::uuid uuid = boost::uuids::random_generator()();
				recordingId = boost::uuids::to_string(uuid);
					
				std::cout << "NEW RECORDING STARTED: " << uuid << std::endl;

				bridge->Play("sound:tt-monkeys");
				bridge->Record(recordingId, "wav");
			}
		});

		client.OnEvent("ChannelDtmfReceived", [&](const aricpp::JsonTree& event) {
			auto digit = aricpp::Get<std::string>(event, { "digit" });
			if (digit == "*") {
				std::cout << "PAUSING RECORDING " << recordingId << std::endl;
				recordingControl.Pause(recordingId);
			}
		});
	});

	ios.run();
}

When trying to Pause the recording, by calling recordingControl.Pause(recordingId) in the snippet above, this exception is raised:

For reference:

  • the recording is created (I am able to see it by calling the GET method to list all live recordings):
  • using Boost 1.66.0

Any idea why this is happening? Am I using the Recording functionality wrong here?

Automatically reconnect websocket if/when connection is lost

The aricpp::Client constructor creates a Websocket connection to get events from the Asterisk server. This is meant to be a long term connection to the server; however, in production environments the websocket can get disconnected. From that point on, no messages will be received from Asterisk.

This issue is related to the following discussion:
#31

Customized application in c to access Asterisk-ARI

Hi Experts,

I am completely new to the asterisk and i want to play with asterisk with my customized application.
I have gone through the wiki.asterisk.org and got knowledge on dial plans, extensions, channels and bridges.

I built asterisk 13 on pc. From here i stuck with few points.

  1. Is asterisk 13 inbuilt consists of ARI library for example ari-py for python. If not how to get that library
    and integrate with asterisk.
  2. Where to run my customized application lets say custom.py to interact with ari interface.

Thanks in advance

Coroutine interface

Add a coroutine interface, to simplify the async programming, emulating a sequential one.

Version file and directory structure conflict with standard library

The current directory setup forces us to add the repositories top level directory to the include search dirs. (for making #include <aricpp/channel.h> working) This makes aricpp's version file getting included by #include <version> on case insensitive file systems (like the default file systems in macOS or Windows). However, this conflicts with clang's libc++ standard library which uses exactly this include for obtaining its own version information.

My proposal is to move the aricpp/aricpp folder to aricpp/include/aricpp. This way, we can add aricpp/include to our compile flags and only add the aricpp headers to our search paths.

Any thoughts or comments? Otherwise I'll go ahead and prepare a PR for this.

Stasis "application" parameter

Currently, Client can receive events from just one stasis application (first problem?) but one must specify the application as a parameter of Channel::Call (second problem?)

Authentication of http requests

Check the authentication mechanism in HttpClient class.

At the first request, the client directly submit the authorization.

Maybe it should first issue a simple request then, once receiving a negative response, it should send a new request with the authorization field.

Improve the Error enum

Currently, the Error enum distinguish just the following cases:

  • none
  • network
  • unknown

It would be useful to add all the various errors that can occour during ARI requests.

The main issue is that each ARI request has its own error, mapped on the same http status.

Ability to use wss and https

In environments where security is required, we need to be able to connect to Asterisk using secure web socket (wss) and send/receive POST requests over https.
The application should provide the certificates to use to connect to the asterisk server, pass them to the client so it can be used by the httpclient and the websocket classes

Error with new versions of boost beast

Discussed in #67

Originally posted by DVPOM November 7, 2023
Hello Daniele,

Sorry to bother you.
I wanted to try ARICPP to connect to asterisk .

But I see below error

[root] ~/ari_112/aricpp-1.1.2/build > make all
[ 6%] Building CXX object examples/CMakeFiles/query.dir/query.cpp.o
In file included from /root/ari_112/aricpp-1.1.2/examples/../include/aricpp/client.h:42,
from /root/ari_112/aricpp-1.1.2/examples/query.cpp:36:
/root/ari_112/aricpp-1.1.2/examples/../include/aricpp/httpclient.h: In lambda function:
/root/ari_112/aricpp-1.1.2/examples/../include/aricpp/httpclient.h:220:62: error: ‘using string_view = boost::core::string_view’ {aka ‘class boost::core::basic_string_view’} has no member named ‘to_string’
220 | CallBack(e, resp.result(), resp.reason().to_string(), resp.body());
| ^~~~~~~~~
make[2]: *** [examples/CMakeFiles/query.dir/build.make:76: examples/CMakeFiles/query.dir/query.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:112: examples/CMakeFiles/query.dir/all] Error 2
make: *** [Makefile:136: all] Error 2
[root] ~/ari_112/aricpp-1.1.2/build > g++ --version
g++ (GCC) 10.2.1 20210130 (Red Hat 10.2.1-11)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
build_examples.txt

Channel recording would not stop

I can start bridge and channel recordings, but only bridge recordings can be stopped.
On channels the stop command would succeed but then not actually work.
After this a channel play would also fail.

I found one missing character, after digging through Asterisk debug logs, that actually fixes everything.
My company prefers I not submit patches but I could not leave you hanging.
Beside you probably want to consider if anything else needs to be done.

In your channel.h line 360 change Recording recording(name, client); to Recording recording(_name, client);

aricpp-1.1.1 and Asterisk 18.9.0

Channel is not copyable

The aricpp::Channel class is not default-copyable because of the constant id member. Clang warns about this while gcc seems to ignore the const.

Clang error message is (with -Werror):

<path redacted>/aricpp/channel.h:102:14: error: explicitly defaulted move assignment operator is implicitly deleted [-Werror,-Wdefaulted-function-deleted]
    Channel& operator=(Channel&& rhs) = default;
             ^
<path redacted>/aricpp/channel.h:479:23: note: move assignment operator of 'Channel' is implicitly deleted because field 'id' has no move assignment operator
    const std::string id;
                      ^

The fix is pretty simple. Just forbid copying. Aricpp is using shared pointers for Channel objects, hence copying is not required.

--- a/aricpp/channel.h
+++ b/aricpp/channel.h
@@ -99,7 +99,7 @@ public:
     Channel(const Channel& rhs) = delete;
     Channel& operator=(const Channel& rhs) = delete;
     Channel(Channel&& rhs) = default;
-    Channel& operator=(Channel&& rhs) = default;
+    Channel& operator=(Channel&& rhs) = delete;

     ~Channel()
     {

High level interface with (custom) futures

I'd like to write a sequence of aricpp operation in this way:

ch1->Hangup()
.Then([=](){ return bridge->Destroy(); })
.Then([=](){ return ch2->Hold(); })
.Then([=](){ cout << "ch2 in hold\n"; })
.OnError([](exception const& e) {cerr << e.what() << endl; });

Call not found error on high level dial

Hi
As i tried to test with samples but it's always throws error as call not found with channel ID value for each channel event including ringing event.
Can you help me to identify the Active/Inactive call with this aricpp by listening channel event.
Using asterisk 16.4.0

Error in compiling samples

Hi, I have a question. First of all thanks for all the good work. I'm willing to use this great library for my project. I am familiar with C++ but the 5 mins tutorial confused me a little bit. How can I install boost library in CentOS 7 and compile the examples?
I installed boost library on CentOS 7 by yum install boost boost-thread boost-devel. Then I tried to make the samples in aricpp/samples directory. But I get the following error:
[root@vmcentos1 samples]# make
g++ -O3 -Werror -Wall -Wextra -Wpedantic -std=c++1y -I.. low_level_dial.cpp -lboost_program_options -lboost_system -lpthread -o low_level_dial
In file included from ../aricpp/client.h:43:0,
from low_level_dial.cpp:42:
../aricpp/websocket.h:41:32: fatal error: boost/beast/core.hpp: No such file or directory
#include <boost/beast/core.hpp>
^
compilation terminated.
make: *** [low_level_dial] Error 1
Any help is appreciated.
Thanks in advanced...

JsonTree::Get cannot get array

My use case was getting the first argument of the Stasis args array:

auto caller_ani = aricpp::Get<std::string>(event, { "args", "[0]" });

This resulted in this output:

Exception in handler of event: StasisStart: No such node (args.[0])

After further investigation, I noticed that JsonTree::Get only takes into account key-value types (this is likely not the best wording) i.e. aricpp::Get<std::string>(event, {"channel", "id"}). This is because it automatically adds dot to the JSON tree i.e. the second parameter to aricpp::Get.

It would be nice to be able to access arrays within an event.

Allow custom logging function in aricpp::Client

Discussed in #57

Originally posted by ferchor2003 June 23, 2021
In my existing application I already have a custom mechanism to log information. I want to use that in my aricpp application instead of simply writing to std::out.
To that effect I have modified client.h and websocket.h to use a function of my choosing to log events with an added severity parameter.
Hopefully you will find this useful.

websocket.h

Add the LoggingSeverity and pLogInfoFnType definitions and calls to the new logging function:

namespace aricpp
{
enum class LoggingSeverity { Info, Warning, Error };
using pLogInfoFnType = std::function< void(const std::string& msg, LoggingSeverity sev)>;

class WebSocket
{
public:

    using ConnectHandler = std::function< void( const boost::system::error_code& ) >;
    using ReceiveHandler = std::function< void( const std::string&, const boost::system::error_code& ) >;

    WebSocket( boost::asio::io_service& _ios, std::string _host, std::string _port, pLogInfoFnType pLogInfoFn) :
        ios(_ios), 
        host(std::move(_host)), 
        port(std::move(_port)), 
        resolver(ios), 
        socket( new socket_type(ios)),
        websocket( new socket_stream_type( *socket)),
        pingTimer(ios),
        LogCallback(pLogInfoFn)
    {}

Where LogCallback is a private member of the class:

	pLogInfoFnType LogCallback;

I can then log events like in the following, replacing std::cout:

    void Received( boost::system::error_code ec )
    {
        std::string s( (std::istreambuf_iterator<char>(&rxData)), std::istreambuf_iterator<char>() );
#ifdef ARICPP_TRACE_WEBSOCKET
        if ( ec ) LogCallback("*** websocket error: " + ec.message() + '\n', LoggingSeverity::Info);
        else LogCallback("*** <== " + s + '\n', LoggingSeverity::Info);
#endif
        if ( ec ) 
			onReceive( std::string(), ec );
        else 
			onReceive( s, ec );
        rxData.consume( rxData.size() );
        if ( ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted ) Read();
    }

client.h

Add the ability to pass the custom logging function as a parameter to the constructor:

Client(boost::asio::io_service& ios, const std::string& host, const std::string& port,
	std::string _user, std::string _password, std::string _application, pLogInfoFnType pLogInfoFn) :
	user(std::move(_user)), password(std::move(_password)),
	application(std::move(_application)),
	websocket(ios, host, port, pLogInfoFn),
	httpclient(ios, host, port, user, password, pLogInfoFn),
	LogCallback(pLogInfoFn)
{
	assert(LogCallback != nullptr);
}

~Client() noexcept
{
    LogCallback("~Client() - Close websocket", LoggingSeverity::Info);
    Close();
}

void Connect( ConnectHandler h, std::size_t connectionRetrySeconds )
{
    onConnection = std::move(h);
    LogCallback("Sending websocket connect request", LoggingSeverity::Info);
    websocket.Connect("/ari/events?api_key="+user+":"+password+"&app="+application+"&subscribeAll=false", 
	[this](auto e)
	{
		if (e)
		{
			std::string errMsg = "websocket connect failure: " + e.message();					
			LogCallback(errMsg, LoggingSeverity::Error);
			onConnection(e);
		}
                else this->WebsocketConnected(); // gcc requires "this"
            },
	connectionRetrySeconds );
}

Application code

// 
// Allows the aricpp::client class to log information with the application
//
void LogInPlugin(const string& msg, aricpp::LoggingSeverity sev)
{
	switch (sev) {
	case aricpp::LoggingSeverity::Info:
		MyLogInfo(msg);
		break;
	case aricpp::LoggingSeverity::Warning:
		MyLogWarning(msg);
		break;
	case aricpp::LoggingSeverity::Error:
		MyLogError(msg);
		break;
	default:
		assert(true);	// Don't know this severity setting
	}
}
...
client = make_unique<aricpp::Client>(ios, astHost, astPort, astUser, astPassword, astAppName, LogInPlugin);

```</div>

in Bridge::StartMoh the parameter mohClass does not work

the concatination of the mohClass parameter has no "=" between the mohClass and the name of the moh class

old: if ( !mohClass.empty() ) query += "?mohClass" + mohClass;

new: if ( !mohClass.empty() ) query += "?mohClass=" + mohClass;

Using std::chrono::seconds instead of int as Channel::Record parameters

As in the subject.
See post in discussions:

I wanna share something that I have found really useful in my usage of the library.

I have found that in my code I have configuration parameters that are sometimes expressed in milliseconds and sometimes in seconds; I want to make sure that when reading the code I know what time units I'm dealing with.

I have modified the Channel::Record for this purpose like this:

#if __cplusplus <= 201103L
	// Simpler, more expressive version working on C++ 11
	ProxyPar<Recording>& Record(const std::string& name, const std::string& format,
		const std::chrono::seconds& maxDurationSeconds = std::chrono::seconds::zero(),
		const std::chrono::seconds& maxSilenceSeconds = std::chrono::seconds::zero(),
		const std::string& ifExists = {}, bool beep = false, const std::string& terminateOn = "none") const
	{
		Recording recording(name, client);
		return ProxyPar<Recording>::Command(
			Method::post,
			"/ari/channels/" + id + "/record?"
			"name=" + UrlEncode(name) +
			"&format=" + format +
			"&terminateOn=" + terminateOn +
			(beep ? "&beep=true" : "&beep=false") +
			(ifExists.empty() ? "" : "&ifExists=" + ifExists) +
			(maxDurationSeconds == std::chrono::seconds::zero() ? "" : "&maxDurationSeconds=" + std::to_string(maxDurationSeconds.count())) +
			(maxSilenceSeconds == std::chrono::seconds::zero() ? "" : "&maxSilenceSeconds=" + std::to_string(maxSilenceSeconds.count())),
			client,
			recording
		);
	}
#else
  // C++ 17 version
#endif

Originally posted by @ferchor2003 in #34

Problem in JsonTree.h ToString() does not work as expected.

When trying to use
inline std::string ToString(const JsonTree& e)
it uses the wrong template version of boost::property_tree::write_json(os, e);

I have fixed it like this:

inline std::string ToString(const JsonTree& e)
{ 
    std::ostringstream os;
    boost::property_tree::write_json(os, e);
    return os.str();
}

DTMF question

My aricpp application has subscribed for and receives DTMF events from one inbound call. However after dialing an outbound call with both answered channels added to a bridge then DTMF events are no longer received but trap shows they are passed between endpoints.

I suspect this has something to do with bridge type "mixing,dtmf_events" but I do not see how to set that except with client::RawCmd post ari/bridges type list. But then I could no longer use your bridge object and would have to use RawCmd with bridge ID for all other bridge commands. I'm just looking for guidance before making that change.

I am using the latest version of aricpp and Asterisk 18.
I have not been able to find anything on this question and would appreciate any help.

Error during compilation samples

Hello!
I using CentOS 7, g++ version 4.8.5
I tried to compile samples using makefile which include to sample directory and receved lot of errrors like as following:

In file included from ../aricpp/httpclient.h:43:0,
from ../aricpp/client.h:44,
from low_level_dial.cpp:42:
../aricpp/method.h: In function ‘std::string aricpp::ToString(aricpp::Method)’:
../aricpp/method.h:61:27: error: expected type-specifier
return d[ static_cast<std::underlying_type_t>(m) ];
^
../aricpp/method.h:61:27: error: expected ‘>’
../aricpp/method.h:61:27: error: expected ‘(’
../aricpp/method.h:61:27: error: ‘underlying_type_t’ is not a member of ‘std’
../aricpp/method.h:61:56: error: expected primary-expression before ‘>>’ token
return d[ static_cast<std::underlying_type_t>(m) ];
^
../aricpp/method.h:61:62: error: expected ‘)’ before ‘]’ token
return d[ static_cast<std::underlying_type_t>(m) ];

Could you help me undarsand what is wrong?

Thank you in adwance.

V. 1.0.0 does not compile with boost libraries v. 1.66.0

Error message:

In file included from /home/sadel/lib/aricpp/aricpp/client.h:44:0,
                 from /home/sadel/lib/aricpp/aricpp/bridge.h:37,
                 from /home/sadel/lib/aricpp/aricpp/arimodel.h:41,
                 from /home/sadel/modules/cti/pbx.h:13,
                 from /home/sadel/modules/cti/ctimanager.cpp:9:
/home/sadel/lib/aricpp/aricpp/websocket.h: In lambda function:
/home/sadel/lib/aricpp/aricpp/websocket.h:121:36: error: 'const error_code' has no member named 'failed'
                     if (error_code.failed())

It seems that boost::system::error_code does not have method failed() in v. 1.66

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.