Changed name to client and changed test program to not include reddit
This commit is contained in:
parent
4d7a91738e
commit
74c8948139
452
doc/examples/reddit_test.cpp
Normal file
452
doc/examples/reddit_test.cpp
Normal file
File diff suppressed because one or more lines are too long
@ -32,13 +32,13 @@
|
||||
|
||||
namespace matrix{
|
||||
struct auth_data{
|
||||
raii::rjp_string bot_name;
|
||||
raii::rjp_string bot_pass;
|
||||
raii::rjp_string name;
|
||||
raii::rjp_string pass;
|
||||
raii::rjp_string homeserver;
|
||||
raii::rjp_string access_token;
|
||||
|
||||
operator bool(void)const{
|
||||
return (bot_name && bot_pass && homeserver);
|
||||
return (name && pass && homeserver);
|
||||
}
|
||||
};
|
||||
|
||||
@ -136,7 +136,7 @@ namespace matrix{
|
||||
};
|
||||
|
||||
//main class
|
||||
class bot
|
||||
class client
|
||||
{
|
||||
private:
|
||||
class mat_url_list
|
||||
@ -207,18 +207,18 @@ 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 membership_info&)> m_membership_callback;
|
||||
std::function<void(const client&,const msg_info&)> m_message_callback;
|
||||
std::function<void(const client&, const membership_info&)> m_membership_callback;
|
||||
|
||||
public:
|
||||
bot(const auth_data& a, const raii::string_base& useragent);
|
||||
bot(const auth_data& a, raii::string&& useragent);
|
||||
bot(const bot& b) = default;
|
||||
bot(bot&& b) = default;
|
||||
~bot(void) = default;
|
||||
client(const auth_data& a, const raii::string_base& useragent);
|
||||
client(const auth_data& a, raii::string&& useragent);
|
||||
client(const client& b) = default;
|
||||
client(client&& b) = default;
|
||||
~client(void) = default;
|
||||
|
||||
bot& operator=(const bot&) = default;
|
||||
bot& operator=(bot&&) = default;
|
||||
client& operator=(const client&) = default;
|
||||
client& operator=(client&&) = default;
|
||||
|
||||
//local getter
|
||||
const raii::rjp_string& access_token(void)const;
|
||||
@ -268,7 +268,6 @@ namespace matrix{
|
||||
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const;
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
|
||||
|
||||
template<class Func>
|
||||
void set_message_callback(Func&& f){
|
||||
|
||||
@ -1,164 +0,0 @@
|
||||
/**
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/string_base.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/curler.hpp"
|
||||
|
||||
namespace reddit{
|
||||
struct auth_data{
|
||||
raii::rjp_string bot_name;
|
||||
raii::rjp_string bot_pass;
|
||||
raii::rjp_string acc_name;
|
||||
raii::rjp_string acc_pass;
|
||||
|
||||
operator bool(void)const{
|
||||
return (bot_name && bot_pass && acc_name && acc_pass);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace time{
|
||||
class period{
|
||||
protected:
|
||||
const char* data;
|
||||
public:
|
||||
constexpr period(const char* d):
|
||||
data(d){}
|
||||
constexpr const char* get(void)const{
|
||||
return data;
|
||||
}
|
||||
};
|
||||
extern period hour;
|
||||
extern period day;
|
||||
extern period week;
|
||||
extern period month;
|
||||
extern period year;
|
||||
extern period all;
|
||||
}
|
||||
|
||||
enum class post_type{
|
||||
image, link, text, video, audio, unrecognized
|
||||
};
|
||||
|
||||
class post
|
||||
{
|
||||
private:
|
||||
enum post_flags{
|
||||
POST_FLAGS_NONE = 0,
|
||||
POST_FLAGS_CROSSPOSTED = 1
|
||||
};
|
||||
private:
|
||||
raii::string m_post;
|
||||
raii::rjp_string m_media_url;
|
||||
raii::string m_hosted_video_audio;
|
||||
raii::rjp_string m_author;
|
||||
raii::rjp_string m_post_hint;
|
||||
raii::rjp_string m_title;
|
||||
raii::rjp_string m_name;
|
||||
raii::rjp_string m_post_url;
|
||||
post_type m_type = post_type::unrecognized;
|
||||
int m_flags = POST_FLAGS_NONE;
|
||||
|
||||
public:
|
||||
post(void) = default;
|
||||
post(const raii::string_base& p);
|
||||
post(raii::string_base&& p);
|
||||
post(const post& p) = default;
|
||||
post(post&& p) = default;
|
||||
~post(void) = default;
|
||||
|
||||
post& operator=(const raii::string_base& p);
|
||||
post& operator=(const post& p) = default;
|
||||
post& operator=(post&& p) = default;
|
||||
|
||||
operator bool(void)const;
|
||||
|
||||
const raii::string& raw(void)const;
|
||||
const raii::rjp_string& mediaurl(void)const;
|
||||
const raii::string& hosted_video_audio(void)const;
|
||||
const raii::rjp_string& posturl(void)const;
|
||||
const raii::rjp_string& author(void)const;
|
||||
const raii::rjp_string& post_hint(void)const;
|
||||
const raii::rjp_string& title(void)const;
|
||||
const raii::rjp_string& name(void)const;
|
||||
bool is_crosspost(void)const;
|
||||
post_type type(void)const;
|
||||
private:
|
||||
void _parse_post(void);
|
||||
static post_type _handle_reddit_hosted_video(RJP_value* data, raii::rjp_string& media_url, raii::string& audio_url);
|
||||
};
|
||||
|
||||
class bot
|
||||
{
|
||||
private:
|
||||
raii::curler m_curl;
|
||||
raii::string m_useragent;
|
||||
raii::rjp_string m_access_token;
|
||||
|
||||
public:
|
||||
bot(const auth_data& a, const raii::string_base& useragent);
|
||||
bot(const auth_data& a, raii::string_base&& useragent);
|
||||
bot(const bot& b);
|
||||
bot(bot&& b);
|
||||
~bot(void) = default;
|
||||
|
||||
bot& operator=(const bot& b);
|
||||
bot& operator=(bot&& b);
|
||||
|
||||
const raii::rjp_string& access_token(void)const;
|
||||
const raii::string& useragent(void)const;
|
||||
void set_useragent(const raii::string_base&);
|
||||
void set_useragent(raii::string_base&&);
|
||||
|
||||
void refresh_token(const auth_data& a);
|
||||
|
||||
post get_new_post(const raii::string_base& subreddit);
|
||||
post get_new_post(const raii::string_base& subreddit, const raii::string_base& after);
|
||||
post get_hot_post(const raii::string_base& subreddit);
|
||||
post get_hot_post(const raii::string_base& subreddit, const raii::string_base& after);
|
||||
post get_rising_post(const raii::string_base& subreddit);
|
||||
post get_rising_post(const raii::string_base& subreddit, const raii::string_base& after);
|
||||
post get_best_post(const raii::string_base& subreddit);
|
||||
post get_best_post(const raii::string_base& subreddit, const raii::string_base& after);
|
||||
post get_top_post(const raii::string_base& subreddit, time::period period = time::day);
|
||||
post get_top_post(const raii::string_base& subreddit, const raii::string_base& after, time::period period = time::day);
|
||||
post get_controversial_post(const raii::string_base& subreddit, time::period period = time::day);
|
||||
post get_controversial_post(const raii::string_base& subreddit, const raii::string_base& after, time::period period = time::day);
|
||||
|
||||
protected:
|
||||
static size_t _get_response_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
|
||||
static raii::curl_llist _create_auth_header(const raii::string_base& access_token);
|
||||
post _get_post(const raii::string_base& subreddit, const raii::string_base& category, const raii::string_base& extradata);
|
||||
void _setup_subreddit_get_curl(const raii::curl_llist& header, const raii::string_base& url, const raii::string_base& reply);
|
||||
|
||||
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
|
||||
static raii::string _create_request_post_data(const raii::string_base& acc_name, const raii::string_base& acc_pass);
|
||||
static raii::string _create_request_userpwd(const raii::string_base& bot_name, const raii::string_base& bot_pass);
|
||||
void _setup_token_request_curl(const raii::string_base& userpwd, const raii::string_base& postdata, void* result);
|
||||
|
||||
raii::string _request_access_token(const auth_data& a);
|
||||
raii::rjp_string _acquire_access_token(const auth_data& a);
|
||||
};
|
||||
|
||||
|
||||
auth_data parse_auth_data(RJP_value* root);
|
||||
|
||||
}
|
||||
2
makefile
2
makefile
@ -26,7 +26,7 @@ CXXFLAGS:=-g -std=c++17 -Wall -pedantic -Wextra
|
||||
all: CXXFLAGS+=-O0
|
||||
release: CXXFLAGS+=-O2
|
||||
LDFLAGS=
|
||||
LDLIBS:=-lcurl -lrjp -lavformat -lavcodec -lavutil -lswresample -lswscale -lfreeimageplus
|
||||
LDLIBS:=-lcurl -lrjp -lavformat -lavcodec -lavutil -lswresample -lswscale -lfreeimageplus -lpthread
|
||||
STRIP:=strip
|
||||
|
||||
memchk:LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||
|
||||
180
src/matrix.cpp
180
src/matrix.cpp
@ -1,5 +1,5 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
This file is a part of rexy's matrix client
|
||||
Copyright (C) 2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -60,14 +60,14 @@ namespace matrix{
|
||||
details[3].value};
|
||||
}
|
||||
|
||||
bot::bot(const auth_data& a, const raii::string_base& useragent):
|
||||
client::client(const auth_data& a, const raii::string_base& useragent):
|
||||
m_curl(),
|
||||
m_useragent(useragent),
|
||||
m_homeserver(a.homeserver)
|
||||
{
|
||||
_acquire_access_token(a);
|
||||
}
|
||||
bot::bot(const auth_data& a, raii::string&& useragent):
|
||||
client::client(const auth_data& a, raii::string&& useragent):
|
||||
m_curl(),
|
||||
m_useragent(std::move(useragent)),
|
||||
m_homeserver(a.homeserver)
|
||||
@ -75,40 +75,40 @@ namespace matrix{
|
||||
_acquire_access_token(a);
|
||||
}
|
||||
|
||||
const raii::rjp_string& bot::access_token(void)const{
|
||||
const raii::rjp_string& client::access_token(void)const{
|
||||
return m_access_token;
|
||||
}
|
||||
const raii::rjp_string& bot::userid(void)const{
|
||||
const raii::rjp_string& client::userid(void)const{
|
||||
return m_userid;
|
||||
}
|
||||
const raii::string& bot::useragent(void)const{
|
||||
const raii::string& client::useragent(void)const{
|
||||
return m_useragent;
|
||||
}
|
||||
void bot::set_useragent(const raii::string_base& useragent){
|
||||
void client::set_useragent(const raii::string_base& useragent){
|
||||
m_useragent = useragent;
|
||||
}
|
||||
void bot::set_useragent(raii::string&& useragent){
|
||||
void client::set_useragent(raii::string&& useragent){
|
||||
m_useragent = std::move(useragent);
|
||||
}
|
||||
|
||||
void bot::set_display_name(const raii::string_base& newname){
|
||||
void client::set_display_name(const raii::string_base& newname){
|
||||
raii::string reply = _put_curl(raii::string("{\"displayname\":\"" + newname + "\"}"), m_urls.displayname(), raii::curl_llist());
|
||||
}
|
||||
void bot::set_profile_picture(const raii::string_base& media_url){
|
||||
void client::set_profile_picture(const raii::string_base& media_url){
|
||||
raii::string reply = _put_curl(raii::string("{\"avatar_url\":\"" + media_url + "\"}"), m_urls.profile_picture(), raii::curl_llist());
|
||||
}
|
||||
|
||||
raii::rjp_string bot::get_display_name(void)const{
|
||||
raii::rjp_string client::get_display_name(void)const{
|
||||
return _get_and_find(m_urls.displayname(), "displayname"_ss);
|
||||
}
|
||||
raii::rjp_string bot::get_profile_picture(void)const{
|
||||
raii::rjp_string client::get_profile_picture(void)const{
|
||||
return _get_and_find(m_urls.profile_picture(), "avatar_url"_ss);
|
||||
}
|
||||
raii::rjp_string bot::room_alias_to_id(const raii::string_base& alias)const{
|
||||
raii::rjp_string client::room_alias_to_id(const raii::string_base& alias)const{
|
||||
auto tmp = m_curl.encode(alias, alias.length());
|
||||
return _get_and_find(raii::string(m_urls.alias_lookup() + tmp), "room_id"_ss);
|
||||
}
|
||||
std::vector<raii::rjp_string> bot::list_rooms(void)const{
|
||||
std::vector<raii::rjp_string> client::list_rooms(void)const{
|
||||
std::vector<raii::rjp_string> ret;
|
||||
raii::string reply = _get_curl(m_urls.room_list());
|
||||
if(!reply)
|
||||
@ -128,7 +128,7 @@ namespace matrix{
|
||||
return ret;
|
||||
}
|
||||
|
||||
raii::string bot::create_room(const raii::string_base& name, const raii::string_base& alias)const{
|
||||
raii::string client::create_room(const raii::string_base& name, const raii::string_base& alias)const{
|
||||
raii::string postdata;
|
||||
if(alias)
|
||||
postdata = "{\"name\": \"" + raii::json_escape(name) + "\",\"room_alias_name\": \"" + raii::json_escape(alias) + "\"}";
|
||||
@ -137,28 +137,28 @@ namespace matrix{
|
||||
|
||||
return _post_curl(postdata, m_urls.create_room(), raii::curl_llist());
|
||||
}
|
||||
bool bot::join_room(const raii::string_base& roomid)const{
|
||||
bool client::join_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_urls.join_room(m_homeserver, m_access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool bot::leave_room(const raii::string_base& roomid)const{
|
||||
bool client::leave_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_urls.leave_room(m_homeserver, m_access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool bot::accept_invite(const membership_info& invite)const{
|
||||
bool client::accept_invite(const membership_info& invite)const{
|
||||
return join_room(invite.roomid);
|
||||
}
|
||||
bool bot::reject_invite(const membership_info& invite)const{
|
||||
bool client::reject_invite(const membership_info& invite)const{
|
||||
return leave_room(invite.roomid);
|
||||
}
|
||||
file_info bot::upload_file(const raii::string_base& filename)const{
|
||||
file_info client::upload_file(const raii::string_base& filename)const{
|
||||
return upload_file(filename, raii::static_string());
|
||||
}
|
||||
file_info bot::upload_file(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
file_info client::upload_file(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
raii::filerd fd(filename);
|
||||
if(!fd) return {};
|
||||
|
||||
return file_info{_upload_file(fd, raii::curl_llist{}), alias ? alias : filename, {}, fd.length()};
|
||||
}
|
||||
image_info bot::upload_image(const raii::string_base& filename)const{
|
||||
image_info client::upload_image(const raii::string_base& filename)const{
|
||||
return upload_image(filename, raii::static_string());
|
||||
}
|
||||
#ifdef HAS_FREEIMAGE
|
||||
@ -201,7 +201,7 @@ namespace matrix{
|
||||
}
|
||||
return {};
|
||||
}
|
||||
image_info bot::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
image_info ret;
|
||||
FREE_IMAGE_FORMAT type = fipImage::identifyFIF(filename.get());
|
||||
ret.mimetype = FreeImage_GetFIFMimeType(type);
|
||||
@ -231,16 +231,16 @@ namespace matrix{
|
||||
}
|
||||
|
||||
#else //HAS_FREEIMAGE
|
||||
image_info bot::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
image_info ret = {};
|
||||
ret = upload_file(filename, alias);
|
||||
return ret;
|
||||
}
|
||||
#endif //HAS_FREEIMAGE
|
||||
video_info bot::upload_video(const raii::string_base& filename)const{
|
||||
video_info client::upload_video(const raii::string_base& filename)const{
|
||||
return upload_video(filename, raii::static_string());
|
||||
}
|
||||
audio_info bot::upload_audio(const raii::string_base& filename)const{
|
||||
audio_info client::upload_audio(const raii::string_base& filename)const{
|
||||
return upload_audio(filename, raii::static_string());
|
||||
}
|
||||
#ifdef HAS_FFMPEG
|
||||
@ -327,7 +327,7 @@ namespace matrix{
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name, first - ctx->iformat->name));
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name));
|
||||
}
|
||||
video_info bot::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
video_info ret = {};
|
||||
|
||||
libav::fmt::input_context in(filename);
|
||||
@ -376,7 +376,7 @@ namespace matrix{
|
||||
ret.filename = alias ? alias : filename;
|
||||
return ret;
|
||||
}
|
||||
audio_info bot::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
audio_info ret = {};
|
||||
|
||||
libav::fmt::input_context in(filename);
|
||||
@ -393,41 +393,41 @@ namespace matrix{
|
||||
return ret;
|
||||
}
|
||||
#else //HAS_FFMPEG
|
||||
video_info bot::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
video_info ret = {};
|
||||
ret = upload_file(filename, alias);
|
||||
return ret;
|
||||
}
|
||||
audio_info bot::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
audio_info ret = {};
|
||||
ret = upload_file(filename, alias);
|
||||
return ret;
|
||||
}
|
||||
#endif //HAS_FFMPEG
|
||||
|
||||
raii::rjp_string bot::send_file(const raii::string_base& room, const file_info& file)const{
|
||||
raii::rjp_string client::send_file(const raii::string_base& room, const file_info& file)const{
|
||||
return _send_message(room, detail::_file_body(file));
|
||||
}
|
||||
raii::rjp_string bot::send_image(const raii::string_base& room, const image_info& image)const{
|
||||
raii::rjp_string client::send_image(const raii::string_base& room, const image_info& image)const{
|
||||
return _send_message(room, detail::_image_body(image));
|
||||
}
|
||||
raii::rjp_string bot::send_video(const raii::string_base& room, const video_info& video)const{
|
||||
raii::rjp_string client::send_video(const raii::string_base& room, const video_info& video)const{
|
||||
return _send_message(room, detail::_video_body(video));
|
||||
}
|
||||
raii::rjp_string bot::send_audio(const raii::string_base& room, const audio_info& audio)const{
|
||||
raii::rjp_string client::send_audio(const raii::string_base& room, const audio_info& audio)const{
|
||||
return _send_message(room, detail::_audio_body(audio));
|
||||
}
|
||||
raii::rjp_string bot::send_message(const raii::string_base& room, const raii::string_base& text)const{
|
||||
raii::rjp_string client::send_message(const raii::string_base& room, const raii::string_base& text)const{
|
||||
return _send_message(room, detail::_message_body(text));
|
||||
}
|
||||
void bot::send_typing(const raii::string_base& room, bool active, int timeout)const{
|
||||
void client::send_typing(const raii::string_base& room, bool active, int timeout)const{
|
||||
if(active)
|
||||
_put_curl(raii::string("{\"timeout\":" + raii::itostr(timeout) + ",\"typing\":true}"), m_urls.typing(m_homeserver, m_access_token, m_curl.encode(room), m_curl.encode(m_userid)), raii::curl_llist());
|
||||
else
|
||||
_put_curl("{\"typing\":false}"_ss, m_urls.typing(m_homeserver, m_access_token, m_curl.encode(room), m_curl.encode(m_userid)), raii::curl_llist());
|
||||
}
|
||||
|
||||
raii::rjp_string bot::redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const{
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const{
|
||||
auto ret = _put_curl(raii::string("{\"reason\":\"" + reason + "\"}"), m_urls.redact(m_homeserver, m_access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
if(!ret) return {};
|
||||
raii::rjp_ptr root(rjp_parse(ret.get()));
|
||||
@ -437,15 +437,15 @@ namespace matrix{
|
||||
if(!res.value) return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
raii::rjp_string bot::redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return redact_event(roomid, eventid, ""_ss);
|
||||
}
|
||||
|
||||
void bot::logout(void){
|
||||
void client::logout(void){
|
||||
_get_curl(m_urls.logout(m_homeserver, m_access_token));
|
||||
m_urls.invalidate_accesstoken();
|
||||
}
|
||||
raii::string bot::sync(size_t timeout){
|
||||
raii::string client::sync(size_t timeout){
|
||||
raii::string reply = _get_curl(m_urls.sync(m_homeserver, m_access_token, m_next_batch, raii::itostr(timeout)));
|
||||
|
||||
if(!reply)
|
||||
@ -474,7 +474,7 @@ namespace matrix{
|
||||
Internal functions
|
||||
********************************/
|
||||
|
||||
void bot::_handle_membership_events(RJP_value* rooms){
|
||||
void client::_handle_membership_events(RJP_value* rooms){
|
||||
RJP_search_res res = rjp_search_member(rooms, "invite", 0);
|
||||
if(res.value)
|
||||
_handle_invites(res.value);
|
||||
@ -482,7 +482,7 @@ namespace matrix{
|
||||
if(res.value)
|
||||
_handle_other_membership(res.value);
|
||||
}
|
||||
void bot::_handle_other_membership(RJP_value* join){
|
||||
void client::_handle_other_membership(RJP_value* join){
|
||||
for(RJP_value* roomid = rjp_get_member(join);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
@ -508,7 +508,7 @@ namespace matrix{
|
||||
}
|
||||
}
|
||||
}
|
||||
void bot::_handle_invites(RJP_value* invites){
|
||||
void client::_handle_invites(RJP_value* invites){
|
||||
for(RJP_value* roomid = rjp_get_member(invites);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "invite_state", 0);
|
||||
if(!res.value) continue;
|
||||
@ -525,7 +525,7 @@ namespace matrix{
|
||||
}
|
||||
}
|
||||
}
|
||||
void bot::_handle_messages(RJP_value* messages){
|
||||
void client::_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)){
|
||||
@ -555,10 +555,10 @@ namespace matrix{
|
||||
}
|
||||
}
|
||||
|
||||
void bot::_send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
void client::_send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
_post_curl(""_ss, m_urls.read_receipt(m_homeserver, m_access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
}
|
||||
raii::rjp_string bot::_upload_file(raii::filerd& fp, const raii::curl_llist& header)const{
|
||||
raii::rjp_string client::_upload_file(raii::filerd& fp, const raii::curl_llist& header)const{
|
||||
raii::string fileurl;
|
||||
m_curl.postreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, NULL);
|
||||
@ -584,7 +584,7 @@ namespace matrix{
|
||||
|
||||
return res.value;
|
||||
}
|
||||
raii::rjp_string bot::_send_message(const raii::string_base& room, const raii::string_base& msg)const{
|
||||
raii::rjp_string client::_send_message(const raii::string_base& room, const raii::string_base& msg)const{
|
||||
raii::rjp_string reply = _post_and_find(
|
||||
msg,
|
||||
m_urls.send(m_homeserver, m_access_token, m_curl.encode(room)),
|
||||
@ -592,13 +592,13 @@ namespace matrix{
|
||||
"event_id"_ss);
|
||||
return reply;
|
||||
}
|
||||
size_t bot::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
size_t client::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||
(*data) += ptr;
|
||||
return size*nmemb;
|
||||
}
|
||||
|
||||
raii::string bot::_get_curl(const raii::string_base& url)const{
|
||||
raii::string client::_get_curl(const raii::string_base& url)const{
|
||||
raii::string reply;
|
||||
m_curl.getreq();
|
||||
m_curl.seturl(url);
|
||||
@ -610,7 +610,7 @@ namespace matrix{
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::string bot::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string client::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
m_curl.postreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, postdata.get());
|
||||
@ -637,7 +637,7 @@ namespace matrix{
|
||||
src->data += to_copy;
|
||||
return to_copy;
|
||||
}
|
||||
raii::string bot::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string client::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
put_data data{putdata.get(), putdata.length()};
|
||||
m_curl.putreq();
|
||||
@ -657,7 +657,7 @@ namespace matrix{
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::rjp_string bot::_post_and_find(const raii::string_base& data, const raii::string_base& url,
|
||||
raii::rjp_string client::_post_and_find(const raii::string_base& data, const raii::string_base& url,
|
||||
const raii::curl_llist& header, const raii::string_base& target)const
|
||||
{
|
||||
raii::string reply = _post_curl(data, url, header);
|
||||
@ -665,13 +665,13 @@ namespace matrix{
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string bot::_get_and_find(const raii::string_base& url, const raii::string_base& target)const{
|
||||
raii::rjp_string client::_get_and_find(const raii::string_base& url, const raii::string_base& target)const{
|
||||
raii::string reply = _get_curl(url);
|
||||
if(!reply)
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string bot::_curl_reply_search(const raii::string_base& reply, const raii::string_base& target)const{
|
||||
raii::rjp_string client::_curl_reply_search(const raii::string_base& reply, const raii::string_base& target)const{
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return {};
|
||||
@ -680,7 +680,7 @@ namespace matrix{
|
||||
return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
void bot::_set_curl_defaults(void)const{
|
||||
void client::_set_curl_defaults(void)const{
|
||||
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
|
||||
m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
m_curl.setuseragent(m_useragent);
|
||||
@ -690,9 +690,9 @@ namespace matrix{
|
||||
m_curl.setopt(CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
m_curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
}
|
||||
raii::string bot::_request_access_token(const auth_data& a)const{
|
||||
raii::string client::_request_access_token(const auth_data& a)const{
|
||||
CURLcode result;
|
||||
raii::string postdata("{\"type\":\"m.login.password\", \"user\":\"" + raii::json_escape(a.bot_name) + "\", \"password\":\"" + raii::json_escape(a.bot_pass) + "\"}");
|
||||
raii::string postdata("{\"type\":\"m.login.password\", \"user\":\"" + raii::json_escape(a.name) + "\", \"password\":\"" + raii::json_escape(a.pass) + "\"}");
|
||||
raii::string reply;
|
||||
|
||||
m_curl.seturl(m_urls.login());
|
||||
@ -708,7 +708,7 @@ namespace matrix{
|
||||
|
||||
return reply;
|
||||
}
|
||||
void bot::_get_new_access_token(const auth_data& a){
|
||||
void client::_get_new_access_token(const auth_data& a){
|
||||
m_urls = mat_url_list(m_homeserver);
|
||||
raii::string reply = _request_access_token(a);
|
||||
if(!reply)
|
||||
@ -723,7 +723,7 @@ namespace matrix{
|
||||
m_urls.repopulate_accesstoken(m_homeserver, m_access_token);
|
||||
m_urls.repopulate_userid(m_homeserver, m_access_token, m_curl.encode(m_userid));
|
||||
}
|
||||
void bot::_acquire_access_token(const auth_data& a){
|
||||
void client::_acquire_access_token(const auth_data& a){
|
||||
_set_curl_defaults();
|
||||
if(a.access_token){
|
||||
m_access_token = a.access_token;
|
||||
@ -750,28 +750,28 @@ namespace matrix{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bot::mat_url_list::mat_url_list(const raii::string_base& homeserver){
|
||||
client::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, const raii::string_base& userid){
|
||||
client::mat_url_list::mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
repopulate(homeserver, access_token, userid);
|
||||
}
|
||||
void bot::mat_url_list::repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
void client::mat_url_list::repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
m_create_room = s_proto + homeserver + "/_matrix/client/r0/createRoom?access_token=" + access_token;
|
||||
m_file_upload = s_proto + homeserver + "/_matrix/media/r0/upload?access_token=" + access_token;
|
||||
m_room_list = s_proto + homeserver + "/_matrix/client/r0/joined_rooms?access_token=" + access_token;
|
||||
m_whoami = s_proto + homeserver + "/_matrix/client/r0/account/whoami?access_token=" + access_token;
|
||||
}
|
||||
void bot::mat_url_list::repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
void client::mat_url_list::repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
m_displayname = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/displayname?access_token=" + access_token;
|
||||
m_profile_picture = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/avatar_url?access_token=" + access_token;
|
||||
}
|
||||
void bot::mat_url_list::repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
void client::mat_url_list::repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
_initial_populate(homeserver);
|
||||
repopulate_accesstoken(homeserver, access_token);
|
||||
repopulate_userid(homeserver, access_token, userid);
|
||||
}
|
||||
void bot::mat_url_list::invalidate_accesstoken(void){
|
||||
void client::mat_url_list::invalidate_accesstoken(void){
|
||||
m_create_room.reset();
|
||||
m_file_upload.reset();
|
||||
m_room_list.reset();
|
||||
@ -779,81 +779,81 @@ namespace matrix{
|
||||
m_displayname.reset();
|
||||
m_profile_picture.reset();
|
||||
}
|
||||
const raii::string& bot::mat_url_list::create_room(void)const{
|
||||
const raii::string& client::mat_url_list::create_room(void)const{
|
||||
return m_create_room;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::file_upload(void)const{
|
||||
const raii::string& client::mat_url_list::file_upload(void)const{
|
||||
return m_file_upload;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::room_list(void)const{
|
||||
const raii::string& client::mat_url_list::room_list(void)const{
|
||||
return m_room_list;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::login(void)const{
|
||||
const raii::string& client::mat_url_list::login(void)const{
|
||||
return m_login;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::alias_lookup(void)const{
|
||||
const raii::string& client::mat_url_list::alias_lookup(void)const{
|
||||
return m_alias_lookup;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::whoami(void)const{
|
||||
const raii::string& client::mat_url_list::whoami(void)const{
|
||||
return m_whoami;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::displayname(void)const{
|
||||
const raii::string& client::mat_url_list::displayname(void)const{
|
||||
return m_displayname;
|
||||
}
|
||||
const raii::string& bot::mat_url_list::profile_picture(void)const{
|
||||
const raii::string& client::mat_url_list::profile_picture(void)const{
|
||||
return m_profile_picture;
|
||||
}
|
||||
raii::string bot::mat_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
raii::string client::mat_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/logout?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/join?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/leave?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const{
|
||||
raii::string client::mat_url_list::sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const{
|
||||
if(!next_batch)
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout);
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout + "&since=" + next_batch);
|
||||
}
|
||||
raii::string bot::mat_url_list::read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
raii::string client::mat_url_list::read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/receipt/m.read/" + eventid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/send/m.room.message?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
raii::string client::mat_url_list::redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/redact/" + eventid + "/0?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/state/m.room.power_levels?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const{
|
||||
raii::string client::mat_url_list::presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/presence/" + userid + "/status?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const{
|
||||
raii::string client::mat_url_list::typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/typing/" + userid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/kick?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/ban?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/unban?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/invite?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
raii::string client::mat_url_list::room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/members?access_token=" + access_token);
|
||||
}
|
||||
raii::string bot::mat_url_list::password(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
raii::string client::mat_url_list::password(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/account/password?access_token=" + access_token);
|
||||
}
|
||||
void bot::mat_url_list::_initial_populate(const raii::string_base& homeserver){
|
||||
void client::mat_url_list::_initial_populate(const raii::string_base& homeserver){
|
||||
m_alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
|
||||
m_login = s_proto + homeserver + "/_matrix/client/r0/login";
|
||||
}
|
||||
|
||||
534
src/reddit.cpp
534
src/reddit.cpp
@ -1,534 +0,0 @@
|
||||
/**
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "reddit.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
|
||||
#include <algorithm> //search
|
||||
#include <cstring>
|
||||
|
||||
//no idea if this covers everything reddit might dish out at me
|
||||
//there is no consistency in their content tagging. there are gifs marked as images, others as videos
|
||||
//they separate audio and video streams for their hosted videos, there is no true way to tell
|
||||
//what kind of content a post contains since the post_hint field might be completely nonexistent.
|
||||
//it's just a game of hacking together solutions each time reddit throws me a new type of unexpected complication.
|
||||
|
||||
namespace reddit{
|
||||
|
||||
namespace time{
|
||||
period hour = "hour";
|
||||
period day = "day";
|
||||
period week = "week";
|
||||
period month = "month";
|
||||
period year = "year";
|
||||
period all = "all";
|
||||
}
|
||||
|
||||
auth_data parse_auth_data(RJP_value* root){
|
||||
static const char* account_names[2] = {"bot", "account"};
|
||||
static const char* account_fields[2] = {"username", "password"};
|
||||
|
||||
auth_data ret;
|
||||
RJP_search_res accounts[2];
|
||||
RJP_search_res details[2];
|
||||
rjp_search_members(root, 2, account_names, accounts, 0);
|
||||
|
||||
rjp_search_members(accounts[0].value, 2, account_fields, details, 0);
|
||||
ret.bot_name = details[0].value;
|
||||
ret.bot_pass = details[1].value;
|
||||
|
||||
rjp_search_members(accounts[1].value, 2, account_fields, details, 0);
|
||||
ret.acc_name = details[0].value;
|
||||
ret.acc_pass = details[1].value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static raii::rjp_string media_search(RJP_value* media){
|
||||
if(!media)
|
||||
return {};
|
||||
RJP_search_res res = rjp_search_member(media, "reddit_video", 0);
|
||||
if(!res.value)
|
||||
return raii::rjp_string{};
|
||||
res = rjp_search_member(res.value, "fallback_url", 0);
|
||||
if(!res.value)
|
||||
return raii::rjp_string{};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
static raii::rjp_string preview_search(RJP_value* root){
|
||||
RJP_search_res media = rjp_search_member(root, "preview", 0);
|
||||
if(!media.value)
|
||||
return raii::rjp_string{};
|
||||
media = rjp_search_member(media.value, "reddit_video_preview", 0);
|
||||
if(!media.value)
|
||||
return raii::rjp_string{};
|
||||
media = rjp_search_member(media.value, "fallback_url", 0);
|
||||
if(!media.value)
|
||||
return raii::rjp_string{};
|
||||
return raii::rjp_string(media.value);
|
||||
}
|
||||
static bool check_reddit_media_domain(RJP_value* root){
|
||||
RJP_search_res res = rjp_search_member(root, "is_reddit_media_domain", 0);
|
||||
return (res.value && rjp_value_boolean(res.value));
|
||||
}
|
||||
|
||||
static raii::rjp_string find_video_url(RJP_value* root){
|
||||
RJP_search_res media = rjp_search_member(root, "media", 0);
|
||||
if(raii::rjp_string res = media_search(media.value)){
|
||||
return res;
|
||||
}
|
||||
raii::rjp_string res = preview_search(root);
|
||||
return res;
|
||||
}
|
||||
static bool is_gifv(const raii::string_base& str){
|
||||
const char* s = str.get();
|
||||
size_t len = str.length();
|
||||
if(len > 5 &&
|
||||
*(s+len-1) == 'v' &&
|
||||
*(s+len-2) == 'f' &&
|
||||
*(s+len-3) == 'i' &&
|
||||
*(s+len-4) == 'g' &&
|
||||
*(s+len-5) == '.')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool has_extension(const raii::string_base& str){
|
||||
size_t i = 0;
|
||||
for(const char* p = str.get() + str.length() - 1;*p && i < 6;--p,++i){
|
||||
if(*p == '/')
|
||||
return false;
|
||||
else if(*p == '.')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool is_gfycat_link(const raii::string_base& str){
|
||||
static const char gfycat[] = "gfycat.com";
|
||||
return *std::search(str.get(), str.get()+str.length(), gfycat, gfycat+sizeof(gfycat)-1) != 0;
|
||||
}
|
||||
static bool is_imgur_link(const raii::string_base& str){
|
||||
static const char imgur[] = "imgur.com";
|
||||
return *std::search(str.get(), str.get()+str.length(), imgur, imgur+sizeof(imgur)-1) != 0;
|
||||
}
|
||||
static bool is_direct_imgur_link(const raii::string_base& str){
|
||||
return is_imgur_link(str) && has_extension(str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
post::post(const raii::string_base& p):
|
||||
m_post(p)
|
||||
{
|
||||
_parse_post();
|
||||
}
|
||||
post::post(raii::string_base&& p):
|
||||
m_post(std::move(p)),
|
||||
m_type(post_type::unrecognized)
|
||||
{
|
||||
_parse_post();
|
||||
}
|
||||
post& post::operator=(const raii::string_base& p){
|
||||
post tmp(p);
|
||||
if(!tmp)
|
||||
return *this;
|
||||
return (*this = std::move(tmp));
|
||||
}
|
||||
post::operator bool(void)const{
|
||||
if(m_type == post_type::text)
|
||||
return (m_post_url && m_author && m_title && m_name);
|
||||
else
|
||||
return (m_post_url && m_media_url && m_author && m_title && m_name);
|
||||
}
|
||||
|
||||
const raii::string& post::raw(void)const{
|
||||
return m_post;
|
||||
}
|
||||
const raii::rjp_string& post::mediaurl(void)const{
|
||||
return m_media_url;
|
||||
}
|
||||
const raii::string& post::hosted_video_audio(void)const{
|
||||
return m_hosted_video_audio;
|
||||
}
|
||||
const raii::rjp_string& post::posturl(void)const{
|
||||
return m_post_url;
|
||||
}
|
||||
const raii::rjp_string& post::author(void)const{
|
||||
return m_author;
|
||||
}
|
||||
const raii::rjp_string& post::post_hint(void)const{
|
||||
return m_post_hint;
|
||||
}
|
||||
const raii::rjp_string& post::title(void)const{
|
||||
return m_title;
|
||||
}
|
||||
const raii::rjp_string& post::name(void)const{
|
||||
return m_name;
|
||||
}
|
||||
bool post::is_crosspost(void)const{
|
||||
return (m_flags & POST_FLAGS_CROSSPOSTED);
|
||||
}
|
||||
post_type post::type(void)const{
|
||||
return m_type;
|
||||
}
|
||||
void post::_parse_post(void){
|
||||
raii::rjp_ptr root(rjp_parse(m_post));
|
||||
if(!root)
|
||||
return;
|
||||
|
||||
static const char* search_items[] = {"url", "author", "post_hint", "title", "id", "crosspost_parent_list"};
|
||||
static constexpr size_t num_searches = sizeof(search_items)/sizeof(search_items[0]);
|
||||
RJP_search_res results[num_searches];
|
||||
RJP_search_res data = rjp_search_member(root.get(), "data", 0);
|
||||
if(!data.value) return;
|
||||
data = rjp_search_member(data.value, "children", 0);
|
||||
if(!data.value) return;
|
||||
data.value = rjp_get_element(data.value);
|
||||
if(!data.value) return;
|
||||
RJP_search_res kind = rjp_search_member(data.value, "kind", 0);
|
||||
if(!kind.value) return;
|
||||
data = rjp_search_member(data.value, "data", 0);
|
||||
if(!data.value) return;
|
||||
|
||||
RJP_search_res& crosspost = results[5];
|
||||
|
||||
rjp_search_members(data.value, num_searches, search_items, results, 0);
|
||||
|
||||
//reddit will *sometimes* make the url field point to the crosspost parent's comments page.
|
||||
//so we just always assume that the true link is in the crosspost parent
|
||||
if(crosspost.value){
|
||||
m_flags |= POST_FLAGS_CROSSPOSTED;
|
||||
crosspost.value = rjp_get_element(crosspost.value);
|
||||
crosspost = rjp_search_member(crosspost.value, "url", 0);
|
||||
if(crosspost.value)
|
||||
m_media_url = crosspost.value;
|
||||
}else{
|
||||
m_media_url = results[0].value;
|
||||
}
|
||||
m_author = results[1].value;
|
||||
m_post_hint = results[2].value;
|
||||
m_title = results[3].value;
|
||||
m_name = raii::rjp_string(kind.value) + "_" + rjp_value_string(results[4].value);
|
||||
m_post_url = "https://redd.it/" + raii::rjp_string(results[4].value);
|
||||
|
||||
if(m_post_hint){
|
||||
//handle simple image
|
||||
if(!strcmp(m_post_hint, "image")){
|
||||
m_type = post_type::image;
|
||||
}
|
||||
//handle link
|
||||
else if(!strcmp(m_post_hint, "link")){
|
||||
m_type = post_type::link;
|
||||
|
||||
//imgur support
|
||||
if(is_imgur_link(m_media_url)){
|
||||
if(is_gifv(m_media_url)){ //gifv is a video
|
||||
if(raii::rjp_string tmp = preview_search(data.value)){
|
||||
m_media_url = std::move(tmp);
|
||||
m_type = post_type::video;
|
||||
}
|
||||
}else{
|
||||
//imgur links don't lead to the image source. adding .jpg to the link leads to the source
|
||||
//except when the link is to an album or to a gifv
|
||||
m_media_url += ".jpg"; //imgur is dumb
|
||||
m_type = post_type::image;
|
||||
}
|
||||
//gfycat support
|
||||
}else if(is_gfycat_link(m_media_url)){
|
||||
if(raii::rjp_string tmp = find_video_url(data.value)){
|
||||
m_media_url = std::move(tmp);
|
||||
m_type = post_type::video;
|
||||
}
|
||||
}
|
||||
}
|
||||
//handle hosted video
|
||||
else if(!strcmp(m_post_hint, "hosted:video")){
|
||||
m_type = _handle_reddit_hosted_video(data.value, m_media_url, m_hosted_video_audio);
|
||||
}
|
||||
else if(!strcmp(m_post_hint, "rich:video")){
|
||||
RJP_search_res media = rjp_search_member(data.value, "media", 0);
|
||||
raii::rjp_string res = media_search(media.value);
|
||||
if(res){
|
||||
m_type = post_type::video;
|
||||
m_media_url = std::move(res);
|
||||
return;
|
||||
}
|
||||
res = preview_search(data.value);
|
||||
if(res){
|
||||
m_type = post_type::video;
|
||||
m_media_url = std::move(res);
|
||||
return;
|
||||
}
|
||||
m_type = post_type::link;
|
||||
}
|
||||
else{
|
||||
//assume text post for other
|
||||
m_type = post_type::text;
|
||||
}
|
||||
}else if(is_direct_imgur_link(m_media_url)){
|
||||
m_type = post_type::image;
|
||||
return;
|
||||
}else if(check_reddit_media_domain(data.value)){
|
||||
m_type = _handle_reddit_hosted_video(data.value, m_media_url, m_hosted_video_audio);
|
||||
/*RJP_value* media = rjp_search_member(data.value, "media", 0).value;
|
||||
if(media && (rjp_value_type(media) != json_null))
|
||||
m_type = post_type::video;
|
||||
else
|
||||
m_type = post_type::image;
|
||||
//*/
|
||||
}else{
|
||||
m_media_url.reset();
|
||||
m_type = post_type::text;
|
||||
}
|
||||
}
|
||||
post_type post::_handle_reddit_hosted_video(RJP_value* data, raii::rjp_string& media_url, raii::string& audio_url){
|
||||
|
||||
RJP_search_res media = rjp_search_member(data, "media", 0);
|
||||
RJP_search_res gif = rjp_search_member(media.value, "reddit_video", 0);
|
||||
|
||||
//treat gif as image even though reddit thinks they're videos
|
||||
if(gif.value)
|
||||
gif = rjp_search_member(media.value, "is_gif", 0);
|
||||
if(gif.value && rjp_value_boolean(gif.value)){
|
||||
return post_type::image;
|
||||
}
|
||||
raii::rjp_string res = media_search(media.value);
|
||||
if(!res){
|
||||
res = preview_search(data);
|
||||
if(!res){
|
||||
return post_type::link;
|
||||
}
|
||||
}
|
||||
media_url = std::move(res);
|
||||
|
||||
//reddit hosts audio and video separately. Meaning I have to find a way to manually recombine them.
|
||||
//this sets up a link to the audio source of the video. the video might not actually have audio. when downloading
|
||||
//from the audio link, always make sure to check for 404 errors.
|
||||
static constexpr char url_base[] = "https://v.redd.it/";
|
||||
static constexpr size_t url_base_len = sizeof(url_base)-1;
|
||||
char* end = strstr(media_url.get()+url_base_len, "/");
|
||||
if(!end)
|
||||
end = media_url.get()+media_url.length();
|
||||
size_t len = end - media_url.get();
|
||||
audio_url = raii::string(len + 6);
|
||||
memcpy(audio_url.get(), media_url.get(), len);
|
||||
memcpy(audio_url.get()+len, "/audio", 6);
|
||||
audio_url[len+6] = 0;
|
||||
return post_type::video;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bot::bot(const auth_data& a, const raii::string_base& useragent):
|
||||
m_curl(),
|
||||
m_useragent(useragent),
|
||||
m_access_token(_acquire_access_token(a)){}
|
||||
bot::bot(const auth_data& a, raii::string_base&& useragent):
|
||||
m_curl(),
|
||||
m_useragent(std::move(useragent)),
|
||||
m_access_token(_acquire_access_token(a)){}
|
||||
bot::bot(const bot& b):
|
||||
m_curl(b.m_curl),
|
||||
m_useragent(b.m_useragent),
|
||||
m_access_token(b.m_access_token){}
|
||||
bot::bot(bot&& b):
|
||||
m_curl(std::move(b.m_curl)),
|
||||
m_useragent(std::move(b.m_useragent)),
|
||||
m_access_token(std::move(b.m_access_token)){}
|
||||
|
||||
bot& bot::operator=(bot&& b){
|
||||
m_useragent = std::move(b.m_useragent);
|
||||
m_access_token = std::move(b.m_access_token);
|
||||
return *this;
|
||||
}
|
||||
bot& bot::operator=(const bot& b){
|
||||
bot tmp(b);
|
||||
return *this = std::move(tmp);
|
||||
}
|
||||
|
||||
const raii::rjp_string& bot::access_token(void)const{
|
||||
return m_access_token;
|
||||
}
|
||||
const raii::string& bot::useragent(void)const{
|
||||
return m_useragent;
|
||||
}
|
||||
void bot::set_useragent(const raii::string_base& s){
|
||||
m_useragent = s;
|
||||
}
|
||||
void bot::set_useragent(raii::string_base&& s){
|
||||
m_useragent = std::move(s);
|
||||
}
|
||||
|
||||
void bot::refresh_token(const auth_data& a){
|
||||
m_access_token = _acquire_access_token(a);
|
||||
}
|
||||
|
||||
post bot::get_new_post(const raii::string_base& subreddit){
|
||||
return _get_post(subreddit, "new"_ss, "limit=1"_ss);
|
||||
}
|
||||
post bot::get_new_post(const raii::string_base& subreddit, const raii::string_base& after){
|
||||
return _get_post(subreddit, "new"_ss, raii::string("limit=1&after=" + after));
|
||||
}
|
||||
post bot::get_hot_post(const raii::string_base& subreddit){
|
||||
return _get_post(subreddit, "hot"_ss, "limit=1"_ss);
|
||||
}
|
||||
post bot::get_hot_post(const raii::string_base& subreddit, const raii::string_base& after){
|
||||
return _get_post(subreddit, "hot"_ss, raii::string("limit=1&after=" + after));
|
||||
}
|
||||
post bot::get_rising_post(const raii::string_base& subreddit){
|
||||
return _get_post(subreddit, "rising"_ss, "limit=1"_ss);
|
||||
}
|
||||
post bot::get_rising_post(const raii::string_base& subreddit, const raii::string_base& after){
|
||||
return _get_post(subreddit, "rising"_ss, raii::string("limit=1&after=" + after));
|
||||
}
|
||||
post bot::get_best_post(const raii::string_base& subreddit){
|
||||
return _get_post(subreddit, "best"_ss, "limit=1"_ss);
|
||||
}
|
||||
post bot::get_best_post(const raii::string_base& subreddit, const raii::string_base& after){
|
||||
return _get_post(subreddit, "best"_ss, raii::string("limit=1&after=" + after));
|
||||
}
|
||||
post bot::get_top_post(const raii::string_base& subreddit, time::period period){
|
||||
raii::static_string pstr = period.get();
|
||||
return _get_post(subreddit, "top"_ss, raii::string("limit=1&t=" + pstr));
|
||||
}
|
||||
post bot::get_top_post(const raii::string_base& subreddit, const raii::string_base& after, time::period period){
|
||||
raii::static_string pstr = period.get();
|
||||
return _get_post(subreddit, "top"_ss, raii::string("limit=1&t=" + pstr + "&after=" + after));
|
||||
}
|
||||
post bot::get_controversial_post(const raii::string_base& subreddit, time::period period){
|
||||
raii::static_string pstr = period.get();
|
||||
return _get_post(subreddit, "controversial"_ss, raii::string("limit=1&t=" + pstr));
|
||||
}
|
||||
post bot::get_controversial_post(const raii::string_base& subreddit, const raii::string_base& after, time::period period){
|
||||
raii::static_string pstr = period.get();
|
||||
return _get_post(subreddit, "controversial"_ss, raii::string("limit=1&t=" + pstr + "&after=" + after));
|
||||
}
|
||||
|
||||
|
||||
post bot::_get_post(const raii::string_base& subreddit, const raii::string_base& category, const raii::string_base& extra){
|
||||
raii::string rep;
|
||||
static constexpr char url_base[] = "https://oauth.reddit.com/r/";
|
||||
raii::string url;
|
||||
if(extra)
|
||||
url = (url_base + subreddit) + "/" + category + "?" + extra;
|
||||
else
|
||||
url = (url_base + subreddit) + "/" + category;
|
||||
raii::curl_llist header(_create_auth_header(m_access_token));
|
||||
m_curl.reset();
|
||||
_setup_subreddit_get_curl(header, url, rep);
|
||||
m_curl.perform();
|
||||
return post(rep);
|
||||
}
|
||||
size_t bot::_get_response_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
raii::rjp_string* reply = reinterpret_cast<raii::rjp_string*>(userdata);
|
||||
(*reply) += ptr;
|
||||
return size*nmemb;
|
||||
}
|
||||
raii::curl_llist bot::_create_auth_header(const raii::string_base& access_token){
|
||||
return raii::curl_llist(raii::string("Authorization: bearer " + access_token));
|
||||
}
|
||||
void bot::_setup_subreddit_get_curl(const raii::curl_llist& header, const raii::string_base& url, const raii::string_base& reply){
|
||||
m_curl.seturl(url);
|
||||
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
|
||||
m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
m_curl.setopt(CURLOPT_MAXREDIRS, 50L);
|
||||
m_curl.setopt(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
m_curl.forcessl(CURL_SSLVERSION_TLSv1_2);
|
||||
m_curl.setopt(CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
m_curl.setheader(header);
|
||||
m_curl.setuseragent(m_useragent);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _get_response_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
m_curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
}
|
||||
|
||||
size_t bot::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||
(*data) += ptr;
|
||||
return size*nmemb;
|
||||
}
|
||||
//Create reddit login data
|
||||
raii::string bot::_create_request_post_data(const raii::string_base& account_name, const raii::string_base& account_pass){
|
||||
return raii::string("grant_type=password&username=" + account_name + "&password=" + account_pass);
|
||||
}
|
||||
//Setup login data for reddit bot
|
||||
raii::string bot::_create_request_userpwd(const raii::string_base& bot_name, const raii::string_base& bot_pass){
|
||||
return raii::string(bot_name + ":" + bot_pass);
|
||||
}
|
||||
void bot::_setup_token_request_curl(const raii::string_base& userpwd, const raii::string_base& postdata, void* result){
|
||||
static constexpr char reddit_token_address[] = "https://www.reddit.com/api/v1/access_token";
|
||||
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
|
||||
m_curl.seturl(reddit_token_address);
|
||||
m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
m_curl.setuserpwd(userpwd);
|
||||
m_curl.setpostdata(postdata);
|
||||
m_curl.setuseragent(m_useragent);
|
||||
m_curl.setheader(raii::curl_llist());
|
||||
m_curl.setopt(CURLOPT_MAXREDIRS, 50L);
|
||||
m_curl.setopt(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
m_curl.forcessl(CURL_SSLVERSION_TLSv1_2);
|
||||
m_curl.setopt(CURLOPT_CUSTOMREQUEST, "POST");
|
||||
m_curl.setopt(CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, result);
|
||||
m_curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
}
|
||||
|
||||
raii::string bot::_request_access_token(const auth_data& auth){
|
||||
CURLcode result;
|
||||
|
||||
//URL encode the POST data
|
||||
raii::curl_string acc_name = m_curl.encode(auth.acc_name, auth.acc_name.length());
|
||||
raii::curl_string acc_pass = m_curl.encode(auth.acc_pass, auth.acc_pass.length());
|
||||
|
||||
//unify the post data, clean up remnants
|
||||
raii::string postdata = _create_request_post_data(acc_name, acc_pass);
|
||||
acc_name.reset();
|
||||
acc_pass.reset();
|
||||
|
||||
//Unify the username/password
|
||||
raii::string userpwd = _create_request_userpwd(auth.bot_name, auth.bot_pass);
|
||||
|
||||
//Load curl with data then run POST operation
|
||||
raii::string reply;
|
||||
_setup_token_request_curl(userpwd, postdata, &reply);
|
||||
result = m_curl.perform();
|
||||
|
||||
if(result != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
|
||||
raii::rjp_string bot::_acquire_access_token(const auth_data& a){
|
||||
raii::string reply = _request_access_token(a);
|
||||
if(!reply)
|
||||
return raii::rjp_string{};
|
||||
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return raii::rjp_string{};
|
||||
RJP_search_res token = rjp_search_member(root.get(), "access_token", 0);
|
||||
return raii::rjp_string{token.value};
|
||||
}
|
||||
}
|
||||
462
src/test.cpp
462
src/test.cpp
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user