added invite handling and a couple example programs
This commit is contained in:
3
TODO
3
TODO
@@ -8,10 +8,11 @@ general/other:
|
||||
matrix:
|
||||
thread pool for workers. rehurn std::promise?
|
||||
handle user admin levels
|
||||
ignore own messages
|
||||
ignore own messages/invites
|
||||
handle events other than user messages
|
||||
organize matrix urls. Prevent reallocation of reusable urls.
|
||||
generate nonreusable urls for matrix in an organized manner.
|
||||
ignore old messages based on value of 'age' parameter?
|
||||
|
||||
reddit:
|
||||
handle imgur albums
|
||||
|
||||
49
examples/commands.cpp
Normal file
49
examples/commands.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//example of a bot which responds to commands
|
||||
|
||||
#include "matrix.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
int main(){
|
||||
const char* username = "username";
|
||||
const char* password = "password";
|
||||
const char* useragent = "rexy712s info bot";
|
||||
const char* homeserver = "matrix.org";
|
||||
matrix::auth_data auth{username, password, homeserver};
|
||||
|
||||
matrix::bot matbot(auth, useragent);
|
||||
auto sync_reply = matbot.sync(0); //initial sync
|
||||
|
||||
bool should_quit = false;
|
||||
matbot.set_message_callback([&](const matrix::bot& bot, const matrix::msg_info& msg)->void
|
||||
{
|
||||
if(msg.body == "!exit"_ss){
|
||||
should_quit = true;
|
||||
bot.send_message(msg.roomid, "[INFO] Shutting down..."_ss);
|
||||
}else if(msg.body == "!info"_ss){
|
||||
bot.send_message(msg.roomid, "This is an example of a bot which responds to commands!"_ss);
|
||||
}
|
||||
});
|
||||
|
||||
while(!should_quit){
|
||||
sync_reply = matbot.sync(30000);
|
||||
}
|
||||
|
||||
}
|
||||
41
examples/join.cpp
Normal file
41
examples/join.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Example of a bot which accepts any invite that it is given
|
||||
|
||||
#include "matrix.hpp"
|
||||
|
||||
int main(){
|
||||
const char* username = "username";
|
||||
const char* password = "password";
|
||||
const char* useragent = "rexy712s invite accepter";
|
||||
const char* homeserver = "matrix.org";
|
||||
matrix::auth_data auth{username, password, homeserver};
|
||||
|
||||
|
||||
matrix::bot matbot(auth, useragent);
|
||||
auto sync_reply = matbot.sync(0); //initial sync
|
||||
|
||||
matbot.set_invite_callback([&](const matrix::bot& bot, const matrix::invite_info& invite)->void{
|
||||
bot.join_room(invite.roomid);
|
||||
});
|
||||
|
||||
while(1){
|
||||
sync_reply = matbot.sync(30000);
|
||||
}
|
||||
}
|
||||
@@ -118,11 +118,17 @@ namespace matrix{
|
||||
|
||||
struct msg_info
|
||||
{
|
||||
const raii::rjp_string roomid;
|
||||
const raii::rjp_string sender;
|
||||
const raii::rjp_string& roomid;
|
||||
const raii::rjp_string& sender;
|
||||
const msgtype type;
|
||||
const raii::rjp_string body;
|
||||
const raii::rjp_string eventid;
|
||||
const raii::rjp_string& body;
|
||||
const raii::rjp_string& eventid;
|
||||
};
|
||||
struct invite_info
|
||||
{
|
||||
const raii::rjp_string& roomid;
|
||||
const raii::rjp_string& sender;
|
||||
const raii::rjp_string& eventid;
|
||||
};
|
||||
|
||||
class bot
|
||||
@@ -130,6 +136,7 @@ namespace matrix{
|
||||
private:
|
||||
struct mat_url_list{
|
||||
mat_url_list(void) = default;
|
||||
mat_url_list(const raii::string_base& homeserver);
|
||||
mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token);
|
||||
mat_url_list(const mat_url_list&) = default;
|
||||
mat_url_list(mat_url_list&&) = default;
|
||||
@@ -148,6 +155,7 @@ namespace matrix{
|
||||
raii::string whoami;
|
||||
|
||||
private:
|
||||
void _initial_populate(const raii::string_base& homeserver);
|
||||
static constexpr const char* s_proto = "https://";
|
||||
};
|
||||
private:
|
||||
@@ -161,6 +169,7 @@ namespace matrix{
|
||||
raii::rjp_string m_next_batch; //string which tracks where we are in the server history
|
||||
|
||||
std::function<void(const bot&,const msg_info&)> m_message_callback;
|
||||
std::function<void(const bot&, const invite_info&)> m_invite_callback;
|
||||
|
||||
public:
|
||||
bot(const auth_data& a, const raii::string_base& useragent);
|
||||
@@ -192,6 +201,7 @@ namespace matrix{
|
||||
|
||||
//other networked operations
|
||||
raii::string create_room(const raii::string_base& name, const raii::string_base& alias)const;
|
||||
bool join_room(const raii::string_base& roomid)const;
|
||||
|
||||
//upload media
|
||||
file_info upload_file(const raii::string_base& filename)const;
|
||||
@@ -211,13 +221,19 @@ namespace matrix{
|
||||
raii::rjp_string send_file(const raii::string_base& room, const file_info& file)const;
|
||||
|
||||
template<class Func>
|
||||
void set_sync_callback(Func&& f){
|
||||
void set_message_callback(Func&& f){
|
||||
m_message_callback = std::forward<Func>(f);
|
||||
}
|
||||
template<class Func>
|
||||
void set_invite_callback(Func&& f){
|
||||
m_invite_callback = std::forward<Func>(f);
|
||||
}
|
||||
raii::string sync(size_t timeout);
|
||||
void logout(void);
|
||||
|
||||
protected:
|
||||
void _handle_invites(RJP_value* invites);
|
||||
void _handle_messages(RJP_value* messages);
|
||||
void _send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::rjp_string _upload_file(raii::filerd& fp, const raii::curl_llist& header)const;
|
||||
raii::rjp_string _send_message(const raii::string_base& room, const raii::string_base& msg)const;
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace matrix{
|
||||
details[3].value};
|
||||
}
|
||||
|
||||
bot::mat_url_list::mat_url_list(const raii::string_base& homeserver){
|
||||
_initial_populate(homeserver);
|
||||
}
|
||||
bot::mat_url_list::mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
repopulate(homeserver, access_token);
|
||||
}
|
||||
@@ -63,8 +66,7 @@ namespace matrix{
|
||||
}
|
||||
void bot::mat_url_list::repopulate(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
repopulate_accesstoken(homeserver, access_token);
|
||||
alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
|
||||
login = s_proto + homeserver + "/_matrix/client/r0/login";
|
||||
_initial_populate(homeserver);
|
||||
}
|
||||
void bot::mat_url_list::invalidate_accesstoken(void){
|
||||
create_room.reset();
|
||||
@@ -72,6 +74,10 @@ namespace matrix{
|
||||
room_list.reset();
|
||||
whoami.reset();
|
||||
}
|
||||
void bot::mat_url_list::_initial_populate(const raii::string_base& homeserver){
|
||||
alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
|
||||
login = s_proto + homeserver + "/_matrix/client/r0/login";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +152,10 @@ namespace matrix{
|
||||
|
||||
return _post_curl(postdata, m_urls.create_room, raii::curl_llist());
|
||||
}
|
||||
bool bot::join_room(const raii::string_base& roomid)const{
|
||||
raii::string url = "https://" + m_homeserver + "/_matrix/client/r0/rooms/" + m_curl.encode(roomid) + "/join?access_token=" + m_access_token;
|
||||
return _post_curl(raii::string(), url, raii::curl_llist());
|
||||
}
|
||||
file_info bot::upload_file(const raii::string_base& filename)const{
|
||||
return upload_file(filename, raii::static_string());
|
||||
}
|
||||
@@ -436,14 +446,45 @@ namespace matrix{
|
||||
if(!res.value)
|
||||
return reply;
|
||||
m_next_batch = res.value;
|
||||
if(m_message_callback == nullptr){
|
||||
return reply;
|
||||
}
|
||||
|
||||
res = rjp_search_member(root.get(), "rooms", 0);
|
||||
if(!res.value) return reply;
|
||||
res = rjp_search_member(res.value, "join", 0);
|
||||
if(!res.value) return reply;
|
||||
if(res.value){
|
||||
if(m_message_callback != nullptr)
|
||||
_handle_messages(res.value);
|
||||
if(m_invite_callback != nullptr)
|
||||
_handle_invites(res.value);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
Internal functions
|
||||
********************************/
|
||||
|
||||
void bot::_handle_invites(RJP_value* invites){
|
||||
RJP_search_res res = rjp_search_member(invites, "invite", 0);
|
||||
if(!res.value) return;
|
||||
for(RJP_value* roomid = rjp_get_member(res.value);roomid;roomid = rjp_next_member(roomid)){
|
||||
res = rjp_search_member(roomid, "invite_state", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
static const char* search_terms[] = {"event_id", "sender", "state_key"};
|
||||
static constexpr size_t num_searches = sizeof(search_terms)/sizeof(search_terms[0]);
|
||||
RJP_search_res results[num_searches] = {};
|
||||
rjp_search_members(event, num_searches, search_terms, results, 0);
|
||||
if(!results[0].value || !results[1].value || !results[2].value) continue;
|
||||
if(strcmp(rjp_value_string(results[2].value), m_userid)) continue;
|
||||
raii::rjp_string room_str = rjp_member_name(roomid);
|
||||
m_invite_callback(*this, invite_info{room_str, raii::rjp_string(results[1].value), raii::rjp_string(results[0].value)});
|
||||
}
|
||||
}
|
||||
}
|
||||
void bot::_handle_messages(RJP_value* messages){
|
||||
RJP_search_res res = rjp_search_member(messages, "join", 0);
|
||||
if(!res.value) return;
|
||||
for(RJP_value* roomid = rjp_get_member(res.value);roomid;roomid = rjp_next_member(roomid)){
|
||||
res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
@@ -468,16 +509,8 @@ namespace matrix{
|
||||
_send_read_receipt(room_str, eventid_str);
|
||||
}
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************
|
||||
Internal functions
|
||||
********************************/
|
||||
|
||||
void bot::_send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
raii::string url = "https://" + m_homeserver + "/_matrix/client/r0/rooms/" + m_curl.encode(roomid) + "/receipt/m.read/" + m_curl.encode(eventid) + "?access_token=" + m_access_token;
|
||||
_post_curl(""_ss, url, raii::curl_llist());
|
||||
@@ -646,6 +679,7 @@ namespace matrix{
|
||||
RJP_search_res id = rjp_search_member(root.get(), "user_id", 0);
|
||||
m_userid = raii::rjp_string(id.value);
|
||||
}else{
|
||||
m_urls = mat_url_list(m_homeserver);
|
||||
raii::string reply = _request_access_token(a);
|
||||
if(!reply)
|
||||
return;
|
||||
|
||||
@@ -427,11 +427,16 @@ int main(){
|
||||
bot.send_image(msg.roomid, {"mxc://matrix.org/SYkDDTUwcfscliYTuIfYFIrx"_ss, "nipple.jpg"_ss, "image/jpeg"_ss, 40202, 512, 402, {}, 512, 402, 40202});
|
||||
}
|
||||
};
|
||||
matbot.set_sync_callback(sync_callback);
|
||||
auto invite_callback = [&](const matrix::bot& bot, const matrix::invite_info& invite)->bool{
|
||||
bot.join_room(invite.roomid);
|
||||
return true;
|
||||
};
|
||||
matbot.set_message_callback(sync_callback);
|
||||
matbot.set_invite_callback(invite_callback);
|
||||
|
||||
while(!should_quit){
|
||||
sync_reply = matbot.sync(30000);
|
||||
//DEBUG_PRINT("%s\n", sync_reply.get());
|
||||
DEBUG_PRINT("%s\n", sync_reply.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user