тонна гонвнокода

This commit is contained in:
lost+skunk 2025-03-11 01:01:54 +03:00
parent 5ea0e943fb
commit 6b7bf681ae
25 changed files with 4396 additions and 228 deletions

16
.gitignore vendored
View File

@ -2,15 +2,17 @@
docs.json
__dummy.html
docs/
/skunkybot-d
skunkybot-d.so
skunkybot-d.dylib
skunkybot-d.dll
skunkybot-d.a
skunkybot-d.lib
skunkybot-d-test-*
/neptune
neptune.so
neptune.dylib
neptune.dll
neptune.a
neptune.lib
neptune-test-*
*.exe
*.pdb
*.o
*.db
*.obj
*.lst
config.json

View File

@ -1,7 +1,13 @@
# TODO
- [ ] get rid of indian code
- [x] make function listener asynchronious
- [x] make all as replies
- [x] APOD (https://api.nasa.gov/)
- [ ] implement encrypting
- [ ] implement admin tools (i.e. ban, mute, kick, etc)
- [ ] implement shared-library modules
- [ ] implement config
- [ ] implement forgejo/gitea webhooks
- [ ] implement MSC2448
- [ ] implement rss feeds
# INFO
говнобот
Deps: sqlite3, libolm, libcurl

7
config.example.json Normal file
View File

@ -0,0 +1,7 @@
{
"homeserver": "http://192.168.1.132:8008",
"token": "zfPqqre33zFgLECg71cQxNHdNByDAsvh",
"status": "https://code.lost-skunk.cc/neptune",
"database_path": "matrix.db",
"accept_invitations": true
}

12
dub.sdl
View File

@ -1,6 +1,12 @@
name "skunkybot-d"
name "neptune"
description "Matrix bot"
authors "skunk"
copyright "Copyright © 2025, skunk"
authors "lost+skunk"
copyright "Copyright © 2025, lost+skunk"
license "AGPL-3.0-only"
dependency "gamut" version="~>3.2.0"
dependency "database:sqlite" version="~>1.2.1"
dependency "database" version="~>1.2.1"
dependency "asdf" version="~>0.7.17"
dflags "-L-s"
dflags "-mcpu=native" platform="ldc"
libs "curl"

View File

@ -2,8 +2,19 @@
"fileVersion": 1,
"versions": {
"asdf": "0.7.17",
"automem": "0.6.11",
"cachetools": "0.4.1",
"database": "1.2.1",
"gamut": "3.2.0",
"intel-intrinsics": "1.11.25",
"miniz": "0.0.1",
"mir-algorithm": "3.22.3",
"mir-core": "1.7.1",
"silly": "1.1.1"
"mir-cpuid": "1.2.11",
"mir-ion": "2.3.3",
"requests": "2.1.3",
"silly": "1.1.1",
"test_allocator": "0.3.4",
"unit-threaded": "0.10.8"
}
}

35
source/api.d Normal file
View File

@ -0,0 +1,35 @@
module api;
import util, asdf, api_data;
void send(T)(T content, string roomid, string type = "m.room.message") {
import std.random: uniform;
mkhsrq("/_matrix/client/v3/rooms/" ~ roomid ~ "/send/" ~ type ~
"/skunky-" ~ intToStr(uniform(1111_1111, 9999_9999)), "PUT", content.serializeToJson);
}
void rawState(string room, string name, string content) {
mkhsrq (
"/_matrix/client/v3/rooms/" ~ room ~ "/state/" ~ name,
"PUT",
content
);
}
void state(T)(string room, string name, T content) {
rawState(room, name, content.serializeToJson);
}
T getState(T)(string room, string name) {
return mkhsrq("/_matrix/client/v3/rooms/" ~ room ~ "/state/" ~ name).body.deserialize!T;
}
MSG event(string id, string room) {
struct Evt { MSG content; }
return (mkhsrq("/_matrix/client/v3/rooms/" ~ room ~ "/event/" ~ id).body.deserialize!Evt).content;
}
string upload(T)(T file) {
struct Upload { string content_uri; }
return (mkhsrq("/_matrix/media/v3/upload", "POST", cast(string)file)
.body.deserialize!Upload).content_uri;
}

110
source/api_data.d Normal file
View File

@ -0,0 +1,110 @@
module api_data;
import mir.serde, util: JsonObject;
struct Moderate {
string user_id;
@serdeOptional string reason;
}
struct State {
struct PowerLevels {
short[string] users;
}
struct ACL {
@serdeOptional:
bool allow_ip_literals;
string[] allow = ["*"];
string[] deny;
}
JsonObject content;
string event_id;
string sender;
string type;
}
struct Encrypted {
char[] algorithm;
char[] ciphertext;
char[] device_id;
char[] sender_key;
char[] session_id;
}
struct MSG {
struct Meta {
int h, w;
ulong size;
string mimetype;
}
struct Relates {
struct EvtBase {
string event_id;
}
@serdeOptional @serdeKeys("m.in_reply_to") EvtBase reply;
}
string body;
@serdeOptional @serdeIgnoreDefault string formatted_body;
string msgtype = "m.notice";
@serdeOptional string format = "org.matrix.custom.html";
@serdeOptional @serdeIgnoreDefault:
@serdeKeys("m.relates_to") Relates relates;
string rel_type;
string url;
string filename;
Meta info;
}
struct Sync {
struct StrippedStateEvent {
JsonObject content;
string sender, state_key, type;
}
struct Unsigned {
int age;
string membership, transaction_id;
}
struct Rooms {
struct Invited {
struct IS { StrippedStateEvent[] events; }
IS invite_state;
}
struct Joined {
struct Timeline {
EventWithoutRoomID[] events;
@serdeOptional bool limited;
string prev_batch;
}
struct UNC {
int highlight_count;
int notification_count;
}
@serdeOptional Timeline timeline;
@serdeOptional UNC unread_notifications;
}
@serdeOptional Invited[string] invite;
@serdeOptional Joined[string] join;
}
@serdeOptional Rooms rooms;
string next_batch;
}
struct EventWithoutRoomID {
JsonObject content;
string event_id;
ulong origin_server_ts;
@serdeOptional string sender, state_key, type;
@serdeIgnore string room;
// Unsigned unsigned;
}

View File

@ -1,101 +1,104 @@
module main;
import std.stdio: writeln;
import std.net.curl;
import asdf: deserialize, serializeToJson;
import util;
import listener;
void main() {
import std.process: environment;
alias env = environment.get;
homeserver = env("SKUNKYBOT_HOMESERVER");
init(env("SKUNKYBOT_TOKEN"));
sync;
import database.sqlite.db;
__gshared SQLite3DB db;
int main(string[] args) {
import core.stdc.stdio;
char buf;
char[] fileStr;
auto file = fopen("config.json", "r");
if (file is null) return 1;
while ((buf = cast(char)fgetc(file)) != 255)
fileStr ~= buf;
fclose(file);
cfg = fileStr.deserialize!Config;
cfg.token = "Authorization: Bearer " ~ cfg.token;
if (args.length == 5 && args[1] == "get-token") {
auto rq = mkrqst(
args[2]~"/_matrix/client/v3/login", "POST",
`{
"identifier": {
"type": "m.id.user",
"user": "`~args[3]~`"
},
"initial_device_display_name": "Neptune Bot",
"password": "`~args[4]~`",
"type": "m.login.password"
}`,
true
);
if (rq.status != 200) {
writeln("Something went wrong :(\n", rq.body);
return 1;
}
HTTP http;
string syncUrl;
string initialBatch;
void init(string token) {
http = HTTP();
http.addRequestHeader("Authorization", "Bearer " ~ token);
http.tcpNoDelay = true;
struct Login {
string access_token;
}
// initial sync
writeln("Your access token: \033[0;32m", (rq.body.deserialize!Login).access_token, "\033[0m.\n",
"Save it!");
return 0;
}
init;
import core.thread;
import commands.slaves;
alias sl = commands.slaves;
static foreach (member; __traits(allMembers, sl)) {
static foreach (attr; __traits(getAttributes, __traits(getMember, sl, member))) {
static if (is(attr == Parallel)) {
new Thread(&__traits(getMember, sl, member)).start;
}
}
}
import commands.apod,
commands.misc,
commands.mozhi,
commands.moderation;
listen!(
commands.apod,
commands.misc,
commands.mozhi,
commands.moderation,
);
return 0;
}
// https://m.lost-skunk.cc/_matrix/client/v3/presence/@me:lost-skunk.cc/status
void init() {
db = SQLite3DB(cfg.database_path);
syncUrl = "/_matrix/client/v3/sync?set_presence=online";
db.exec("create table if not exists client (latest_batch string)");
db.exec("create table if not exists room_pls (room string, user string, pl integer)");
auto q = db.query("select latest_batch from client");
if (q.step) initialBatch = q.get!string;
else {
import api_data: Sync;
writeln("starting initial sync..");
syncUrl = homeserver ~ "/_matrix/client/v3/sync?set_presence=online";
initialBatch = (get(syncUrl, http).deserialize!Sync).next_batch; syncUrl ~= "&since=";
Sync snk = mkhsrq(syncUrl).body.deserialize!Sync;
initialBatch = snk.next_batch;
db.exec("insert into client (latest_batch) values (?)", initialBatch);
writeln("done!");
foreach(room; snk.rooms.join.keys) fetchMembers(room);
}
void send(T)(T content, string roomid, string type = "m.room.message") {
import std.random: uniform;
put(homeserver~"/_matrix/client/v3/rooms/" ~ roomid ~ "/send/" ~ type ~
"/skunky-" ~ intToStr(uniform(1111_1111, 9999_9999)), content.serializeToJson, http);
}
// void join(string roomid) {
// post(homeserver~"/_matrix/client/v3/rooms/"~roomid~"/join", http);
// }
import commands;
static MSG helpgen() {
string buf;
static foreach (member; __traits(allMembers, commands)) {
static foreach (attr; __traits(getAttributes, __traits(getMember, commands, member))) {
static if (is(typeof(attr) == Command)) buf ~= member ~ ": " ~ attr.description ~ '\n';
}
}
return MSG(buf);
}
void sync() {
auto content = Sync();
content.next_batch = initialBatch;
for (;;) {
string bthUrl = syncUrl ~ content.next_batch;
try content = get(bthUrl, http).deserialize!Sync;
catch (Exception e) { writeln("ERR: ", e.msg); continue; }
foreach (room, _; content.rooms.invite) {
writeln("Joining to room: ", room);
post(homeserver~"/_matrix/client/v3/rooms/"~room~"/join", null, http);
send(MSG("Йа криведко"), room);
}
foreach (room, roomContent; content.rooms.join) {
foreach(event; roomContent.timeline.events) {
if (event.type == "m.room.message") {
try {
auto evt = deserialize!MSG(event.content.raw);
if (!evt.body.length) break;
auto argz = parseMsg(evt.body);
if (argz.command == "hlp") {
send(helpgen, room);
break;
}
foreach (member; __traits(allMembers, commands)) {
if (argz.command == member) {
alias command = __traits(getMember, commands, member);
foreach (attr; __traits(getAttributes, command)) {
static if (is(typeof(attr) == Command)) {
static if (is(typeof(command) == function))
auto content = command(argz, &event);
else static if (__traits(isStaticArray, command))
auto content = MSG(command[0], command[1]);
else
auto content = MSG(command);
send(content, room);
}
}
}
}
} catch (Exception e) {
writeln(e.msg);
send(MSG(e.msg), room);
}
}
}
}
}
syncUrl ~= "&since=";
}

2223
source/bindings/curl.d Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
/* Copyright 2015-2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module libolm.error;
extern (C):
enum OlmErrorCode
{
OLM_SUCCESS = 0, /*!< There wasn't an error */
OLM_NOT_ENOUGH_RANDOM = 1, /*!< Not enough entropy was supplied */
OLM_OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */
OLM_BAD_MESSAGE_VERSION = 3, /*!< The message version is unsupported */
OLM_BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */
OLM_BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */
OLM_BAD_MESSAGE_KEY_ID = 6, /*!< The message references an unknown key id */
OLM_INVALID_BASE64 = 7, /*!< The input base64 was invalid */
OLM_BAD_ACCOUNT_KEY = 8, /*!< The supplied account key is invalid */
OLM_UNKNOWN_PICKLE_VERSION = 9, /*!< The pickled object is too new */
OLM_CORRUPTED_PICKLE = 10, /*!< The pickled object couldn't be decoded */
OLM_BAD_SESSION_KEY = 11, /*!< Attempt to initialise an inbound group
session from an invalid session key */
OLM_UNKNOWN_MESSAGE_INDEX = 12, /*!< Attempt to decode a message whose
* index is earlier than our earliest
* known session key.
*/
/**
* Attempt to unpickle an account which uses pickle version 1 (which did
* not save enough space for the Ed25519 key; the key should be considered
* compromised. We don't let the user reload the account.
*/
OLM_BAD_LEGACY_ACCOUNT_PICKLE = 13,
/**
* Received message had a bad signature
*/
OLM_BAD_SIGNATURE = 14,
OLM_INPUT_BUFFER_TOO_SMALL = 15,
/**
* SAS doesn't have their key set.
*/
OLM_SAS_THEIR_KEY_NOT_SET = 16,
/**
* The pickled object was successfully decoded, but the unpickling still failed
* because it had some extraneous junk data at the end.
*/
OLM_PICKLE_EXTRA_DATA = 17
/* remember to update the list of string constants in error.c when updating
* this list. */
}
alias OLM_SUCCESS = OlmErrorCode.OLM_SUCCESS;
alias OLM_NOT_ENOUGH_RANDOM = OlmErrorCode.OLM_NOT_ENOUGH_RANDOM;
alias OLM_OUTPUT_BUFFER_TOO_SMALL = OlmErrorCode.OLM_OUTPUT_BUFFER_TOO_SMALL;
alias OLM_BAD_MESSAGE_VERSION = OlmErrorCode.OLM_BAD_MESSAGE_VERSION;
alias OLM_BAD_MESSAGE_FORMAT = OlmErrorCode.OLM_BAD_MESSAGE_FORMAT;
alias OLM_BAD_MESSAGE_MAC = OlmErrorCode.OLM_BAD_MESSAGE_MAC;
alias OLM_BAD_MESSAGE_KEY_ID = OlmErrorCode.OLM_BAD_MESSAGE_KEY_ID;
alias OLM_INVALID_BASE64 = OlmErrorCode.OLM_INVALID_BASE64;
alias OLM_BAD_ACCOUNT_KEY = OlmErrorCode.OLM_BAD_ACCOUNT_KEY;
alias OLM_UNKNOWN_PICKLE_VERSION = OlmErrorCode.OLM_UNKNOWN_PICKLE_VERSION;
alias OLM_CORRUPTED_PICKLE = OlmErrorCode.OLM_CORRUPTED_PICKLE;
alias OLM_BAD_SESSION_KEY = OlmErrorCode.OLM_BAD_SESSION_KEY;
alias OLM_UNKNOWN_MESSAGE_INDEX = OlmErrorCode.OLM_UNKNOWN_MESSAGE_INDEX;
alias OLM_BAD_LEGACY_ACCOUNT_PICKLE = OlmErrorCode.OLM_BAD_LEGACY_ACCOUNT_PICKLE;
alias OLM_BAD_SIGNATURE = OlmErrorCode.OLM_BAD_SIGNATURE;
alias OLM_INPUT_BUFFER_TOO_SMALL = OlmErrorCode.OLM_INPUT_BUFFER_TOO_SMALL;
alias OLM_SAS_THEIR_KEY_NOT_SET = OlmErrorCode.OLM_SAS_THEIR_KEY_NOT_SET;
alias OLM_PICKLE_EXTRA_DATA = OlmErrorCode.OLM_PICKLE_EXTRA_DATA;
/** get a string representation of the given error code. */
const(char)* _olm_error_to_string (OlmErrorCode error);
// extern "C"
/* OLM_ERROR_H_ */

View File

@ -0,0 +1,188 @@
module libolm.inbound;
/* Copyright 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import libolm.error;
@nogc:
extern (C):
struct OlmInboundGroupSession;
/** get the size of an inbound group session, in bytes. */
size_t olm_inbound_group_session_size ();
/**
* Initialise an inbound group session object using the supplied memory
* The supplied memory should be at least olm_inbound_group_session_size()
* bytes.
*/
OlmInboundGroupSession* olm_inbound_group_session (void* memory);
/**
* A null terminated string describing the most recent error to happen to a
* group session */
const(char)* olm_inbound_group_session_last_error (const(OlmInboundGroupSession)* session);
/**
* An error code describing the most recent error to happen to a group
* session */
OlmErrorCode olm_inbound_group_session_last_error_code (const(OlmInboundGroupSession)* session);
/** Clears the memory used to back this group session */
size_t olm_clear_inbound_group_session (OlmInboundGroupSession* session);
/** Returns the number of bytes needed to store an inbound group session */
size_t olm_pickle_inbound_group_session_length (const(OlmInboundGroupSession)* session);
/**
* Stores a group session as a base64 string. Encrypts the session using the
* supplied key. Returns the length of the session on success.
*
* Returns olm_error() on failure. If the pickle output buffer
* is smaller than olm_pickle_inbound_group_session_length() then
* olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
*/
size_t olm_pickle_inbound_group_session (OlmInboundGroupSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/**
* Loads a group session from a pickled base64 string. Decrypts the session
* using the supplied key.
*
* Returns olm_error() on failure. If the key doesn't match the one used to
* encrypt the account then olm_inbound_group_session_last_error() will be
* "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
* olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input
* pickled buffer is destroyed
*/
size_t olm_unpickle_inbound_group_session (OlmInboundGroupSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/**
* Start a new inbound group session, from a key exported from
* olm_outbound_group_session_key
*
* Returns olm_error() on failure. On failure last_error will be set with an
* error code. The last_error will be:
*
* * OLM_INVALID_BASE64 if the session_key is not valid base64
* * OLM_BAD_SESSION_KEY if the session_key is invalid
*/
/* base64-encoded keys */
size_t olm_init_inbound_group_session (OlmInboundGroupSession* session, const(ubyte)* session_key, size_t session_key_length);
/**
* Import an inbound group session, from a previous export.
*
* Returns olm_error() on failure. On failure last_error will be set with an
* error code. The last_error will be:
*
* * OLM_INVALID_BASE64 if the session_key is not valid base64
* * OLM_BAD_SESSION_KEY if the session_key is invalid
*/
/* base64-encoded keys; note that it will be overwritten with the base64-decoded
data. */
size_t olm_import_inbound_group_session (OlmInboundGroupSession* session, const(ubyte)* session_key, size_t session_key_length);
/**
* Get an upper bound on the number of bytes of plain-text the decrypt method
* will write for a given input message length. The actual size could be
* different due to padding.
*
* The input message buffer is destroyed.
*
* Returns olm_error() on failure.
*/
size_t olm_group_decrypt_max_plaintext_length (OlmInboundGroupSession* session, ubyte* message, size_t message_length);
/**
* Decrypt a message.
*
* The input message buffer is destroyed.
*
* Returns the length of the decrypted plain-text, or olm_error() on failure.
*
* On failure last_error will be set with an error code. The last_error will
* be:
* * OLM_OUTPUT_BUFFER_TOO_SMALL if the plain-text buffer is too small
* * OLM_INVALID_BASE64 if the message is not valid base-64
* * OLM_BAD_MESSAGE_VERSION if the message was encrypted with an unsupported
* version of the protocol
* * OLM_BAD_MESSAGE_FORMAT if the message headers could not be decoded
* * OLM_BAD_MESSAGE_MAC if the message could not be verified
* * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the
* message's index (ie, it was sent before the session key was shared with
* us)
*/
/* input; note that it will be overwritten with the base64-decoded
message. */
/* output */
size_t olm_group_decrypt (OlmInboundGroupSession* session, ubyte* message, size_t message_length, ubyte* plaintext, size_t max_plaintext_length, uint* message_index);
/**
* Get the number of bytes returned by olm_inbound_group_session_id()
*/
size_t olm_inbound_group_session_id_length (const(OlmInboundGroupSession)* session);
/**
* Get a base64-encoded identifier for this session.
*
* Returns the length of the session id on success or olm_error() on
* failure. On failure last_error will be set with an error code. The
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
* small.
*/
size_t olm_inbound_group_session_id (OlmInboundGroupSession* session, ubyte* id, size_t id_length);
/**
* Get the first message index we know how to decrypt.
*/
uint olm_inbound_group_session_first_known_index (const(OlmInboundGroupSession)* session);
/**
* Check if the session has been verified as a valid session.
*
* (A session is verified either because the original session share was signed,
* or because we have subsequently successfully decrypted a message.)
*
* This is mainly intended for the unit tests, currently.
*/
int olm_inbound_group_session_is_verified (const(OlmInboundGroupSession)* session);
/**
* Get the number of bytes returned by olm_export_inbound_group_session()
*/
size_t olm_export_inbound_group_session_length (const(OlmInboundGroupSession)* session);
/**
* Export the base64-encoded ratchet key for this session, at the given index,
* in a format which can be used by olm_import_inbound_group_session
*
* Returns the length of the ratchet key on success or olm_error() on
* failure. On failure last_error will be set with an error code. The
* last_error will be:
* * OUTPUT_BUFFER_TOO_SMALL if the buffer was too small
* * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the
* given index (ie, it was sent before the session key was shared with
* us)
*/
size_t olm_export_inbound_group_session (OlmInboundGroupSession* session, ubyte* key, size_t key_length, uint message_index);
// extern "C"
/* OLM_INBOUND_GROUP_SESSION_H_ */

349
source/bindings/olm/olm.d Normal file
View File

@ -0,0 +1,349 @@
/* Copyright 2015, 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import libolm.error;
extern (C):
extern __gshared const size_t OLM_MESSAGE_TYPE_PRE_KEY;
extern __gshared const size_t OLM_MESSAGE_TYPE_MESSAGE;
struct OlmAccount;
struct OlmSession;
struct OlmUtility;
/** Get the version number of the library.
* Arguments will be updated if non-null.
*/
void olm_get_library_version (ubyte* major, ubyte* minor, ubyte* patch);
/** The size of an account object in bytes */
size_t olm_account_size ();
/** The size of a session object in bytes */
size_t olm_session_size ();
/** The size of a utility object in bytes */
size_t olm_utility_size ();
/** Initialise an account object using the supplied memory
* The supplied memory must be at least olm_account_size() bytes */
OlmAccount* olm_account (void* memory);
/** Initialise a session object using the supplied memory
* The supplied memory must be at least olm_session_size() bytes */
OlmSession* olm_session (void* memory);
/** Initialise a utility object using the supplied memory
* The supplied memory must be at least olm_utility_size() bytes */
OlmUtility* olm_utility (void* memory);
/** The value that olm will return from a function if there was an error */
size_t olm_error ();
/** A null terminated string describing the most recent error to happen to an
* account */
const(char)* olm_account_last_error (const(OlmAccount)* account);
/** An error code describing the most recent error to happen to an account */
OlmErrorCode olm_account_last_error_code (const(OlmAccount)* account);
/** A null terminated string describing the most recent error to happen to a
* session */
const(char)* olm_session_last_error (const(OlmSession)* session);
/** An error code describing the most recent error to happen to a session */
OlmErrorCode olm_session_last_error_code (const(OlmSession)* session);
/** A null terminated string describing the most recent error to happen to a
* utility */
const(char)* olm_utility_last_error (const(OlmUtility)* utility);
/** An error code describing the most recent error to happen to a utility */
OlmErrorCode olm_utility_last_error_code (const(OlmUtility)* utility);
/** Clears the memory used to back this account */
size_t olm_clear_account (OlmAccount* account);
/** Clears the memory used to back this session */
size_t olm_clear_session (OlmSession* session);
/** Clears the memory used to back this utility */
size_t olm_clear_utility (OlmUtility* utility);
/** Returns the number of bytes needed to store an account */
size_t olm_pickle_account_length (const(OlmAccount)* account);
/** Returns the number of bytes needed to store a session */
size_t olm_pickle_session_length (const(OlmSession)* session);
/** Stores an account as a base64 string. Encrypts the account using the
* supplied key. Returns the length of the pickled account on success.
* Returns olm_error() on failure. If the pickle output buffer
* is smaller than olm_pickle_account_length() then
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
size_t olm_pickle_account (OlmAccount* account, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** Stores a session as a base64 string. Encrypts the session using the
* supplied key. Returns the length of the pickled session on success.
* Returns olm_error() on failure. If the pickle output buffer
* is smaller than olm_pickle_session_length() then
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
size_t olm_pickle_session (OlmSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** Loads an account from a pickled base64 string. Decrypts the account using
* the supplied key. Returns olm_error() on failure. If the key doesn't
* match the one used to encrypt the account then olm_account_last_error()
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
* olm_account_last_error() will be "INVALID_BASE64". The input pickled
* buffer is destroyed */
size_t olm_unpickle_account (OlmAccount* account, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** Loads a session from a pickled base64 string. Decrypts the session using
* the supplied key. Returns olm_error() on failure. If the key doesn't
* match the one used to encrypt the account then olm_session_last_error()
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
* olm_session_last_error() will be "INVALID_BASE64". The input pickled
* buffer is destroyed */
size_t olm_unpickle_session (OlmSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** The number of random bytes needed to create an account.*/
size_t olm_create_account_random_length (const(OlmAccount)* account);
/** Creates a new account. Returns olm_error() on failure. If there weren't
* enough random bytes then olm_account_last_error() will be
* "NOT_ENOUGH_RANDOM" */
size_t olm_create_account (OlmAccount* account, void* random, size_t random_length);
/** The size of the output buffer needed to hold the identity keys */
size_t olm_account_identity_keys_length (const(OlmAccount)* account);
/** Writes the public parts of the identity keys for the account into the
* identity_keys output buffer. Returns olm_error() on failure. If the
* identity_keys buffer was too small then olm_account_last_error() will be
* "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_account_identity_keys (OlmAccount* account, void* identity_keys, size_t identity_key_length);
/** The length of an ed25519 signature encoded as base64. */
size_t olm_account_signature_length (const(OlmAccount)* account);
/** Signs a message with the ed25519 key for this account. Returns olm_error()
* on failure. If the signature buffer was too small then
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
size_t olm_account_sign (OlmAccount* account, const(void)* message, size_t message_length, void* signature, size_t signature_length);
/** The size of the output buffer needed to hold the one time keys */
size_t olm_account_one_time_keys_length (const(OlmAccount)* account);
/** Writes the public parts of the unpublished one time keys for the account
* into the one_time_keys output buffer.
* <p>
* The returned data is a JSON-formatted object with the single property
* <tt>curve25519</tt>, which is itself an object mapping key id to
* base64-encoded Curve25519 key. For example:
* <pre>
* {
* curve25519: {
* "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
* "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
* }
* }
* </pre>
* Returns olm_error() on failure.
* <p>
* If the one_time_keys buffer was too small then olm_account_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_account_one_time_keys (OlmAccount* account, void* one_time_keys, size_t one_time_keys_length);
/** Marks the current set of one time keys and fallback key as being published
* Once marked as published, the one time keys will no longer be returned by
* olm_account_one_time_keys(), and the fallback key will no longer be returned
* by olm_account_unpublished_fallback_key().
*
* Returns the number of one-time keys that were marked as published. Note that
* this count does not include the fallback key. */
size_t olm_account_mark_keys_as_published (OlmAccount* account);
/** The largest number of one time keys this account can store. */
size_t olm_account_max_number_of_one_time_keys (const(OlmAccount)* account);
/** The number of random bytes needed to generate a given number of new one
* time keys. */
size_t olm_account_generate_one_time_keys_random_length (const(OlmAccount)* account, size_t number_of_keys);
/** Generates a number of new one time keys. If the total number of keys stored
* by this account exceeds max_number_of_one_time_keys() then the old keys are
* discarded. Returns olm_error() on error. If the number of random bytes is
* too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
size_t olm_account_generate_one_time_keys (OlmAccount* account, size_t number_of_keys, void* random, size_t random_length);
/** The number of random bytes needed to generate a fallback key. */
size_t olm_account_generate_fallback_key_random_length (const(OlmAccount)* account);
/** Generates a new fallback key. Only one previous fallback key is
* stored. Returns olm_error() on error. If the number of random bytes is too
* small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
size_t olm_account_generate_fallback_key (OlmAccount* account, void* random, size_t random_length);
/** The number of bytes needed to hold the fallback key as returned by
* olm_account_fallback_key. */
size_t olm_account_fallback_key_length (const(OlmAccount)* account);
/** Deprecated: use olm_account_unpublished_fallback_key instead */
size_t olm_account_fallback_key (OlmAccount* account, void* fallback_key, size_t fallback_key_size);
/** The number of bytes needed to hold the unpublished fallback key as returned
* by olm_account_unpublished fallback_key. */
size_t olm_account_unpublished_fallback_key_length (const(OlmAccount)* account);
/** Returns the fallback key (if present, and if unpublished) into the
* fallback_key buffer */
size_t olm_account_unpublished_fallback_key (OlmAccount* account, void* fallback_key, size_t fallback_key_size);
/** Forget about the old fallback key. This should be called once you are
* reasonably certain that you will not receive any more messages that use
* the old fallback key (e.g. 5 minutes after the new fallback key has been
* published).
*/
void olm_account_forget_old_fallback_key (OlmAccount* account);
/** The number of random bytes needed to create an outbound session */
size_t olm_create_outbound_session_random_length (const(OlmSession)* session);
/** Creates a new out-bound session for sending messages to a given identity_key
* and one_time_key. Returns olm_error() on failure. If the keys couldn't be
* decoded as base64 then olm_session_last_error() will be "INVALID_BASE64"
* If there weren't enough random bytes then olm_session_last_error() will
* be "NOT_ENOUGH_RANDOM". */
size_t olm_create_outbound_session (OlmSession* session, const(OlmAccount)* account, const(void)* their_identity_key, size_t their_identity_key_length, const(void)* their_one_time_key, size_t their_one_time_key_length, void* random, size_t random_length);
/** Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message. Returns olm_error() on failure. If the base64
* couldn't be decoded then olm_session_last_error will be "INVALID_BASE64".
* If the message was for an unsupported protocol version then
* olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message
* couldn't be decoded then olm_session_last_error() will be
* "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time
* key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */
size_t olm_create_inbound_session (OlmSession* session, OlmAccount* account, void* one_time_key_message, size_t message_length);
/** Same as olm_create_inbound_session, but ensures that the identity key
* in the pre-key message matches the expected identity key, supplied via the
* `their_identity_key` parameter. Fails early if there is no match. */
size_t olm_create_inbound_session_from (OlmSession* session, OlmAccount* account, const(void)* their_identity_key, size_t their_identity_key_length, void* one_time_key_message, size_t message_length);
/** The length of the buffer needed to return the id for this session. */
size_t olm_session_id_length (const(OlmSession)* session);
/** An identifier for this session. Will be the same for both ends of the
* conversation. If the id buffer is too small then olm_session_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_session_id (OlmSession* session, void* id, size_t id_length);
int olm_session_has_received_message (const(OlmSession)* session);
/**
* Write a null-terminated string describing the internal state of an olm
* session to the buffer provided for debugging and logging purposes. If the
* buffer is not large enough to hold the entire string, it will be truncated
* and will end with "...". A buffer length of 600 will be enough to hold any
* output.
*/
void olm_session_describe (OlmSession* session, char* buf, size_t buflen);
/** Checks if the PRE_KEY message is for this in-bound session. This can happen
* if multiple messages are sent to this account before this account sends a
* message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
* the session matches. Returns 0 if the session does not match. Returns
* olm_error() on failure. If the base64 couldn't be decoded then
* olm_session_last_error will be "INVALID_BASE64". If the message was for an
* unsupported protocol version then olm_session_last_error() will be
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
size_t olm_matches_inbound_session (OlmSession* session, void* one_time_key_message, size_t message_length);
/** Checks if the PRE_KEY message is for this in-bound session. This can happen
* if multiple messages are sent to this account before this account sends a
* message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
* the session matches. Returns 0 if the session does not match. Returns
* olm_error() on failure. If the base64 couldn't be decoded then
* olm_session_last_error will be "INVALID_BASE64". If the message was for an
* unsupported protocol version then olm_session_last_error() will be
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
size_t olm_matches_inbound_session_from (OlmSession* session, const(void)* their_identity_key, size_t their_identity_key_length, void* one_time_key_message, size_t message_length);
/** Removes the one time keys that the session used from the account. Returns
* olm_error() on failure. If the account doesn't have any matching one time
* keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */
size_t olm_remove_one_time_keys (OlmAccount* account, OlmSession* session);
/** The type of the next message that olm_encrypt() will return. Returns
* OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message.
* Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message.
* Returns olm_error on failure. */
size_t olm_encrypt_message_type (const(OlmSession)* session);
/** The number of random bytes needed to encrypt the next message. */
size_t olm_encrypt_random_length (const(OlmSession)* session);
/** The size of the next message in bytes for the given number of plain-text
* bytes. */
size_t olm_encrypt_message_length (const(OlmSession)* session, size_t plaintext_length);
/** Encrypts a message using the session. Returns the length of the message in
* bytes on success. Writes the message as base64 into the message buffer.
* Returns olm_error() on failure. If the message buffer is too small then
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there
* weren't enough random bytes then olm_session_last_error() will be
* "NOT_ENOUGH_RANDOM". */
size_t olm_encrypt (OlmSession* session, const(void)* plaintext, size_t plaintext_length, void* random, size_t random_length, void* message, size_t message_length);
/** The maximum number of bytes of plain-text a given message could decode to.
* The actual size could be different due to padding. The input message buffer
* is destroyed. Returns olm_error() on failure. If the message base64
* couldn't be decoded then olm_session_last_error() will be
* "INVALID_BASE64". If the message is for an unsupported version of the
* protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION".
* If the message couldn't be decoded then olm_session_last_error() will be
* "BAD_MESSAGE_FORMAT". */
size_t olm_decrypt_max_plaintext_length (OlmSession* session, size_t message_type, void* message, size_t message_length);
/** Decrypts a message using the session. The input message buffer is destroyed.
* Returns the length of the plain-text on success. Returns olm_error() on
* failure. If the plain-text buffer is smaller than
* olm_decrypt_max_plaintext_length() then olm_session_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then
* olm_session_last_error() will be "INVALID_BASE64". If the message is for
* an unsupported version of the protocol then olm_session_last_error() will
* be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then
* olm_session_last_error() will be BAD_MESSAGE_FORMAT".
* If the MAC on the message was invalid then olm_session_last_error() will
* be "BAD_MESSAGE_MAC". */
size_t olm_decrypt (OlmSession* session, size_t message_type, void* message, size_t message_length, void* plaintext, size_t max_plaintext_length);
/** The length of the buffer needed to hold the SHA-256 hash. */
size_t olm_sha256_length (const(OlmUtility)* utility);
/** Calculates the SHA-256 hash of the input and encodes it as base64. If the
* output buffer is smaller than olm_sha256_length() then
* olm_utility_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_sha256 (OlmUtility* utility, const(void)* input, size_t input_length, void* output, size_t output_length);
/** Verify an ed25519 signature. If the key was too small then
* olm_utility_last_error() will be "INVALID_BASE64". If the signature was invalid
* then olm_utility_last_error() will be "BAD_MESSAGE_MAC". */
size_t olm_ed25519_verify (OlmUtility* utility, const(void)* key, size_t key_length, const(void)* message, size_t message_length, void* signature, size_t signature_length);
/* OLM_H_ */

View File

@ -0,0 +1,136 @@
module libolm.outbound;
/* Copyright 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import libolm.error;
extern (C):
struct OlmOutboundGroupSession;
/** get the size of an outbound group session, in bytes. */
size_t olm_outbound_group_session_size ();
/**
* Initialise an outbound group session object using the supplied memory
* The supplied memory should be at least olm_outbound_group_session_size()
* bytes.
*/
OlmOutboundGroupSession* olm_outbound_group_session (void* memory);
/**
* A null terminated string describing the most recent error to happen to a
* group session */
const(char)* olm_outbound_group_session_last_error (const(OlmOutboundGroupSession)* session);
/**
* An error code describing the most recent error to happen to a group
* session */
OlmErrorCode olm_outbound_group_session_last_error_code (const(OlmOutboundGroupSession)* session);
/** Clears the memory used to back this group session */
size_t olm_clear_outbound_group_session (OlmOutboundGroupSession* session);
/** Returns the number of bytes needed to store an outbound group session */
size_t olm_pickle_outbound_group_session_length (const(OlmOutboundGroupSession)* session);
/**
* Stores a group session as a base64 string. Encrypts the session using the
* supplied key. Returns the length of the session on success.
*
* Returns olm_error() on failure. If the pickle output buffer
* is smaller than olm_pickle_outbound_group_session_length() then
* olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
*/
size_t olm_pickle_outbound_group_session (OlmOutboundGroupSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/**
* Loads a group session from a pickled base64 string. Decrypts the session
* using the supplied key.
*
* Returns olm_error() on failure. If the key doesn't match the one used to
* encrypt the account then olm_outbound_group_session_last_error() will be
* "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
* olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
* pickled buffer is destroyed
*/
size_t olm_unpickle_outbound_group_session (OlmOutboundGroupSession* session, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** The number of random bytes needed to create an outbound group session */
size_t olm_init_outbound_group_session_random_length (const(OlmOutboundGroupSession)* session);
/**
* Start a new outbound group session. Returns olm_error() on failure. On
* failure last_error will be set with an error code. The last_error will be
* NOT_ENOUGH_RANDOM if the number of random bytes was too small.
*/
size_t olm_init_outbound_group_session (OlmOutboundGroupSession* session, ubyte* random, size_t random_length);
/**
* The number of bytes that will be created by encrypting a message
*/
size_t olm_group_encrypt_message_length (OlmOutboundGroupSession* session, size_t plaintext_length);
/**
* Encrypt some plain-text. Returns the length of the encrypted message or
* olm_error() on failure. On failure last_error will be set with an
* error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
* buffer is too small.
*/
size_t olm_group_encrypt (OlmOutboundGroupSession* session, const(ubyte)* plaintext, size_t plaintext_length, ubyte* message, size_t message_length);
/**
* Get the number of bytes returned by olm_outbound_group_session_id()
*/
size_t olm_outbound_group_session_id_length (const(OlmOutboundGroupSession)* session);
/**
* Get a base64-encoded identifier for this session.
*
* Returns the length of the session id on success or olm_error() on
* failure. On failure last_error will be set with an error code. The
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
* small.
*/
size_t olm_outbound_group_session_id (OlmOutboundGroupSession* session, ubyte* id, size_t id_length);
/**
* Get the current message index for this session.
*
* Each message is sent with an increasing index; this returns the index for
* the next message.
*/
uint olm_outbound_group_session_message_index (OlmOutboundGroupSession* session);
/**
* Get the number of bytes returned by olm_outbound_group_session_key()
*/
size_t olm_outbound_group_session_key_length (const(OlmOutboundGroupSession)* session);
/**
* Get the base64-encoded current ratchet key for this session.
*
* Each message is sent with a different ratchet key. This function returns the
* ratchet key that will be used for the next message.
*
* Returns the length of the ratchet key on success or olm_error() on
* failure. On failure last_error will be set with an error code. The
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small.
*/
size_t olm_outbound_group_session_key (OlmOutboundGroupSession* session, ubyte* key, size_t key_length);
// extern "C"
/* OLM_OUTBOUND_GROUP_SESSION_H_ */

View File

@ -0,0 +1,37 @@
module bindings.olm;
// import api: Encrypted;
// void decrypt(Encrypted* evt) @nogc {
// import main: session;
// import libolm.inbound;
// import core.stdc.stdlib;
// import core.stdc.string: strcpy;
// // import std.stdio;
// char[] buf = cast(char[])malloc(evt.ciphertext.length)[0..evt.ciphertext.length];
// strcpy(cast(char*)buf, cast(char*)evt.ciphertext);
// size_t mlen = olm_group_decrypt_max_plaintext_length (
// session,
// cast(ubyte*)buf,
// buf.length
// );
// char[] decbuf = cast(char[])malloc(mlen)[0..mlen];
// size_t dcr = olm_group_decrypt (
// session,
// cast(ubyte*)buf,
// buf.length,
// cast(ubyte*)decbuf,
// mlen,
// null
// );
// import core.stdc.stdio;
// printf("%d\n", dcr);
// // // string msg = cast(string)decbuf[0..dcr];
// // writeln(dcr);
// // writeln(msg);
// }

203
source/bindings/olm/pk.d Normal file
View File

@ -0,0 +1,203 @@
/* Copyright 2018, 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import libolm.error;
extern (C):
struct OlmPkEncryption;
/* The size of an encryption object in bytes */
size_t olm_pk_encryption_size ();
/** Initialise an encryption object using the supplied memory
* The supplied memory must be at least olm_pk_encryption_size() bytes */
OlmPkEncryption* olm_pk_encryption (void* memory);
/** A null terminated string describing the most recent error to happen to an
* encryption object */
const(char)* olm_pk_encryption_last_error (const(OlmPkEncryption)* encryption);
/** An error code describing the most recent error to happen to an encryption
* object */
OlmErrorCode olm_pk_encryption_last_error_code (const(OlmPkEncryption)* encryption);
/** Clears the memory used to back this encryption object */
size_t olm_clear_pk_encryption (OlmPkEncryption* encryption);
/** Set the recipient's public key for encrypting to */
size_t olm_pk_encryption_set_recipient_key (OlmPkEncryption* encryption, const(void)* public_key, size_t public_key_length);
/** Get the length of the ciphertext that will correspond to a plaintext of the
* given length. */
size_t olm_pk_ciphertext_length (const(OlmPkEncryption)* encryption, size_t plaintext_length);
/** Get the length of the message authentication code. */
size_t olm_pk_mac_length (const(OlmPkEncryption)* encryption);
/** Get the length of a public or ephemeral key */
size_t olm_pk_key_length ();
/** The number of random bytes needed to encrypt a message. */
size_t olm_pk_encrypt_random_length (const(OlmPkEncryption)* encryption);
/** Encrypt a plaintext for the recipient set using
* olm_pk_encryption_set_recipient_key. Writes to the ciphertext, mac, and
* ephemeral_key buffers, whose values should be sent to the recipient. mac is
* a Message Authentication Code to ensure that the data is received and
* decrypted properly. ephemeral_key is the public part of the ephemeral key
* used (together with the recipient's key) to generate a symmetric encryption
* key. Returns olm_error() on failure. If the ciphertext, mac, or
* ephemeral_key buffers were too small then olm_pk_encryption_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then
* olm_pk_encryption_last_error() will be "OLM_INPUT_BUFFER_TOO_SMALL". */
size_t olm_pk_encrypt (OlmPkEncryption* encryption, const(void)* plaintext, size_t plaintext_length, void* ciphertext, size_t ciphertext_length, void* mac, size_t mac_length, void* ephemeral_key, size_t ephemeral_key_size, const(void)* random, size_t random_length);
struct OlmPkDecryption;
/* The size of a decryption object in bytes */
size_t olm_pk_decryption_size ();
/** Initialise a decryption object using the supplied memory
* The supplied memory must be at least olm_pk_decryption_size() bytes */
OlmPkDecryption* olm_pk_decryption (void* memory);
/** A null terminated string describing the most recent error to happen to a
* decription object */
const(char)* olm_pk_decryption_last_error (const(OlmPkDecryption)* decryption);
/** An error code describing the most recent error to happen to a decription
* object */
OlmErrorCode olm_pk_decryption_last_error_code (const(OlmPkDecryption)* decryption);
/** Clears the memory used to back this decryption object */
size_t olm_clear_pk_decryption (OlmPkDecryption* decryption);
/** Get the number of bytes required to store an olm private key
*/
size_t olm_pk_private_key_length ();
/** DEPRECATED: Use olm_pk_private_key_length()
*/
size_t olm_pk_generate_key_random_length ();
/** Initialise the key from the private part of a key as returned by
* olm_pk_get_private_key(). The associated public key will be written to the
* pubkey buffer. Returns olm_error() on failure. If the pubkey buffer is too
* small then olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
* If the private key was not long enough then olm_pk_decryption_last_error()
* will be "OLM_INPUT_BUFFER_TOO_SMALL".
*
* Note that the pubkey is a base64 encoded string, but the private key is
* an unencoded byte array
*/
size_t olm_pk_key_from_private (OlmPkDecryption* decryption, void* pubkey, size_t pubkey_length, const(void)* privkey, size_t privkey_length);
/** DEPRECATED: Use olm_pk_key_from_private
*/
size_t olm_pk_generate_key (OlmPkDecryption* decryption, void* pubkey, size_t pubkey_length, const(void)* privkey, size_t privkey_length);
/** Returns the number of bytes needed to store a decryption object. */
size_t olm_pickle_pk_decryption_length (const(OlmPkDecryption)* decryption);
/** Stores decryption object as a base64 string. Encrypts the object using the
* supplied key. Returns the length of the pickled object on success.
* Returns olm_error() on failure. If the pickle output buffer
* is smaller than olm_pickle_pk_decryption_length() then
* olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
size_t olm_pickle_pk_decryption (OlmPkDecryption* decryption, const(void)* key, size_t key_length, void* pickled, size_t pickled_length);
/** Loads a decryption object from a pickled base64 string. The associated
* public key will be written to the pubkey buffer. Decrypts the object using
* the supplied key. Returns olm_error() on failure. If the key doesn't
* match the one used to encrypt the account then olm_pk_decryption_last_error()
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
* olm_pk_decryption_last_error() will be "INVALID_BASE64". The input pickled
* buffer is destroyed */
size_t olm_unpickle_pk_decryption (OlmPkDecryption* decryption, const(void)* key, size_t key_length, void* pickled, size_t pickled_length, void* pubkey, size_t pubkey_length);
/** Get the length of the plaintext that will correspond to a ciphertext of the
* given length. */
size_t olm_pk_max_plaintext_length (const(OlmPkDecryption)* decryption, size_t ciphertext_length);
/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. See the
* olm_pk_encrypt function for descriptions of the ephemeral_key and mac
* arguments. Returns the length of the plaintext on success. Returns
* olm_error() on failure. If the plaintext buffer is too small then
* olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_pk_decrypt (OlmPkDecryption* decryption, const(void)* ephemeral_key, size_t ephemeral_key_length, const(void)* mac, size_t mac_length, void* ciphertext, size_t ciphertext_length, void* plaintext, size_t max_plaintext_length);
/**
* Get the private key for an OlmDecryption object as an unencoded byte array
* private_key must be a pointer to a buffer of at least
* olm_pk_private_key_length() bytes and this length must be passed in
* private_key_length. If the given buffer is too small, returns olm_error()
* and olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
* Returns the number of bytes written.
*/
size_t olm_pk_get_private_key (OlmPkDecryption* decryption, void* private_key, size_t private_key_length);
struct OlmPkSigning;
/* The size of a signing object in bytes */
size_t olm_pk_signing_size ();
/** Initialise a signing object using the supplied memory
* The supplied memory must be at least olm_pk_signing_size() bytes */
OlmPkSigning* olm_pk_signing (void* memory);
/** A null terminated string describing the most recent error to happen to a
* signing object */
const(char)* olm_pk_signing_last_error (const(OlmPkSigning)* sign);
/** A null terminated string describing the most recent error to happen to a
* signing object */
OlmErrorCode olm_pk_signing_last_error_code (const(OlmPkSigning)* sign);
/** Clears the memory used to back this signing object */
size_t olm_clear_pk_signing (OlmPkSigning* sign);
/**
* Initialise the signing object with a public/private keypair from a seed. The
* associated public key will be written to the pubkey buffer. Returns
* olm_error() on failure. If the public key buffer is too small then
* olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If the seed
* buffer is too small then olm_pk_signing_last_error() will be
* "INPUT_BUFFER_TOO_SMALL".
*/
size_t olm_pk_signing_key_from_seed (OlmPkSigning* sign, void* pubkey, size_t pubkey_length, const(void)* seed, size_t seed_length);
/**
* The size required for the seed for initialising a signing object.
*/
size_t olm_pk_signing_seed_length ();
/**
* The size of the public key of a signing object.
*/
size_t olm_pk_signing_public_key_length ();
/**
* The size of a signature created by a signing object.
*/
size_t olm_pk_signature_length ();
/**
* Sign a message. The signature will be written to the signature
* buffer. Returns olm_error() on failure. If the signature buffer is too
* small, olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
*/
size_t olm_pk_sign (OlmPkSigning* sign, const(ubyte)* message, size_t message_length, ubyte* signature, size_t signature_length);
/* OLM_PK_H_ */

142
source/bindings/olm/sas.d Normal file
View File

@ -0,0 +1,142 @@
/* Copyright 2018-2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import libolm.error;
extern (C):
/** @defgroup SAS Short Authentication String verification
* These functions are used for verifying keys using the Short Authentication
* String (SAS) method.
* @{
*/
struct OlmSAS;
/** A null terminated string describing the most recent error to happen to an
* SAS object. */
const(char)* olm_sas_last_error (const(OlmSAS)* sas);
/** An error code describing the most recent error to happen to an SAS
* object. */
OlmErrorCode olm_sas_last_error_code (const(OlmSAS)* sas);
/** The size of an SAS object in bytes. */
size_t olm_sas_size ();
/** Initialize an SAS object using the supplied memory.
* The supplied memory must be at least `olm_sas_size()` bytes. */
OlmSAS* olm_sas (void* memory);
/** Clears the memory used to back an SAS object. */
size_t olm_clear_sas (OlmSAS* sas);
/** The number of random bytes needed to create an SAS object. */
size_t olm_create_sas_random_length (const(OlmSAS)* sas);
/** Creates a new SAS object.
*
* @param[in] sas the SAS object to create, initialized by `olm_sas()`.
* @param[in] random array of random bytes. The contents of the buffer may be
* overwritten.
* @param[in] random_length the number of random bytes provided. Must be at
* least `olm_create_sas_random_length()`.
*
* @return `olm_error()` on failure. If there weren't enough random bytes then
* `olm_sas_last_error()` will be `NOT_ENOUGH_RANDOM`.
*/
size_t olm_create_sas (OlmSAS* sas, void* random, size_t random_length);
/** The size of a public key in bytes. */
size_t olm_sas_pubkey_length (const(OlmSAS)* sas);
/** Get the public key for the SAS object.
*
* @param[in] sas the SAS object.
* @param[out] pubkey buffer to store the public key.
* @param[in] pubkey_length the size of the `pubkey` buffer. Must be at least
* `olm_sas_pubkey_length()`.
*
* @return `olm_error()` on failure. If the `pubkey` buffer is too small, then
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
*/
size_t olm_sas_get_pubkey (OlmSAS* sas, void* pubkey, size_t pubkey_length);
/** Sets the public key of other user.
*
* @param[in] sas the SAS object.
* @param[in] their_key the other user's public key. The contents of the
* buffer will be overwritten.
* @param[in] their_key_length the size of the `their_key` buffer.
*
* @return `olm_error()` on failure. If the `their_key` buffer is too small,
* then `olm_sas_last_error()` will be `INPUT_BUFFER_TOO_SMALL`.
*/
size_t olm_sas_set_their_key (OlmSAS* sas, void* their_key, size_t their_key_length);
/** Checks if their key was set.
*
* @param[in] sas the SAS object.
*
*/
int olm_sas_is_their_key_set (const(OlmSAS)* sas);
/** Generate bytes to use for the short authentication string.
*
* @param[in] sas the SAS object.
* @param[in] info extra information to mix in when generating the bytes, as
* per the Matrix spec.
* @param[in] info_length the length of the `info` parameter.
* @param[out] output the output buffer.
* @param[in] output_length the size of the output buffer. For hex-based SAS
* as in the Matrix spec, this will be 5.
*
* @return `olm_error()` on failure. If their key wasn't set then
* `olm_sas_last_error()` will be `SAS_THEIR_KEY_NOT_SET`.
*/
size_t olm_sas_generate_bytes (OlmSAS* sas, const(void)* info, size_t info_length, void* output, size_t output_length);
/** The size of the message authentication code generated by
* olm_sas_calculate_mac()`. */
size_t olm_sas_mac_length (const(OlmSAS)* sas);
/** Generate a message authentication code (MAC) based on the shared secret.
*
* @param[in] sas the SAS object.
* @param[in] input the message to produce the authentication code for.
* @param[in] input_length the length of the message.
* @param[in] info extra information to mix in when generating the MAC, as per
* the Matrix spec.
* @param[in] info_length the length of the `info` parameter.
* @param[out] mac the buffer in which to store the MAC.
* @param[in] mac_length the size of the `mac` buffer. Must be at least
* `olm_sas_mac_length()`
*
* @return `olm_error()` on failure. If the `mac` buffer is too small, then
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
*/
size_t olm_sas_calculate_mac (OlmSAS* sas, const(void)* input, size_t input_length, const(void)* info, size_t info_length, void* mac, size_t mac_length);
// A version of the calculate mac function that produces base64 strings that are
// compatible with other base64 implementations.
size_t olm_sas_calculate_mac_fixed_base64 (OlmSAS* sas, const(void)* input, size_t input_length, const(void)* info, size_t info_length, void* mac, size_t mac_length);
// for compatibility with an old version of Riot
size_t olm_sas_calculate_mac_long_kdf (OlmSAS* sas, const(void)* input, size_t input_length, const(void)* info, size_t info_length, void* mac, size_t mac_length);
/** @} */ // end of SAS group
// extern "C"
/* OLM_SAS_H_ */

View File

@ -1,40 +0,0 @@
module commands;
import util;
// UDA
struct Command {
string description = "No description provided";
string name;
}
@Command("Отображает аватар пользователя")
auto avatar(Arguments argz, EventWithoutRoomID* evt) {
import std.net.curl: get;
string url = cast(string)get(homeserver~"/_matrix/client/v3/profile/" ~
((argz.parsed.length == 0) ? evt.sender : argz.parsed[0]) ~ "/avatar_url");
if (url == "{}") return MSG("User has no avatar");
return MSG(evt.sender, `<img src="`~url[15..$-2]~`">`);
}
@Command("", "пинг")
auto ping(Arguments, EventWithoutRoomID* evt) {
import std.datetime: Clock, SysTime, unixTimeToStdTime;
auto delay = (Clock.currTime() - SysTime(unixTimeToStdTime(0))).total!"msecs" - evt.origin_server_ts;
return MSG("PONG [" ~ intToStr(delay) ~ " ms]");
}
@Command("хз мне лень делать описание")
auto echo(Arguments argz, EventWithoutRoomID* evt) {
return MSG((argz.raw.length > 6) ? argz.raw[6..$] : "Too small MSG");
}
@Command()
string[2] huy = [":orehussmile:",
`<img data-mx-emoticon height="32" alt=":orehussmile:" title=":orehussmile:"
src="mxc://4d2.org/XvWYAuhASYRHtYvtspsrWvtU" >`];
@Command("Версия бота", "версия")
string ver = "SkunkyBot Pre-Alpha 0.1 :: https://git.macaw.me/skunky/skunkybot-d";
@Command("test")
static string compver = "Compiler version: "~intToStr(__VERSION__);

105
source/commands/apod.d Normal file
View File

@ -0,0 +1,105 @@
module commands.apod;
import util, api, api_data;
import commands.util;
import main: db;
struct APOD {
string url;
string hdurl;
string title;
string explanation;
}
struct APOD_internal {
string subscribed_room;
string mxc, title, body, mimetype;
int untill, size, width, height;
}
auto apod_get() {
import asdf, api: upload;
import gamut: Image;
MSG msg;
Image img;
auto data = mkrqst("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY", null, null, true)
.body.deserialize!APOD;
auto image = mkrqst(data.hdurl, null, null, true);
img.loadFromMemory(image.body);
msg.info.w = img.width;
msg.info.h = img.height;
msg.info.size = image.body.length;
msg.info.mimetype = image.getHeader("Content-Type");
msg.url = upload(image.body);
msg.msgtype = "m.image";
msg.filename = data.hdurl;
msg.body = data.title;
msg.formatted_body = "<details><summary><b>"~data.title~"</b></summary><p>"~data.explanation~"</p></details>";
return msg;
}
// 🇮🇳🇮🇳🇮🇳 यह बकवास का एक बुरा टुकड़ा है, लेकिन मैं इसे ठीक करने के लिए बहुत आलसी हूं ।
MSG cached_apod() {
import core.stdc.time: time;
auto now = time(null);
auto q = db.query("select * from apod where subscribed_room = 'CACHE'");
if (q.step) {
APOD_internal data = q.get!APOD_internal;
if (data.untill > now) {
MSG msg = MSG(data.title, data.body);
msg.url = data.mxc;
msg.info.w = data.width;
msg.info.h = data.height;
msg.info.size = data.size;
msg.info.mimetype = data.mimetype;
msg.filename = data.title ~ ".jpg";
msg.msgtype = "m.image";
return msg;
}
}
MSG msg = apod_get;
db.exec("insert into apod
(subscribed_room, width, height, size, mxc, mimetype, title, body, untill)
values ('CACHE',?,?,?,?,?,?,?,?)
on conflict (mxc) do update set
mxc = excluded.mxc,
body = excluded.body,
size = excluded.size,
title = excluded.title,
width = excluded.width,
height = excluded.height,
mimetype = excluded.mimetype",
msg.info.w, msg.info.h, msg.info.size, msg.url, msg.info.mimetype,
msg.body, msg.formatted_body, now + Time.day);
return msg;
}
@Command("Astronomy Picture Of the Day")
auto apod(Arguments arg, EventWithoutRoomID* evt) {
if (arg.parsed.length == 0) return cached_apod;
auto powerlevel = getUsrPL(evt.room, evt.sender);
bool subscribed = db.query("select * from apod where subscribed_room = ?", evt.room).step;
if (powerlevel < 50) return MSG("Unauthorized");
if (arg.parsed[0] == "subscribe") {
if (subscribed)
return MSG("Room is already subscribed to APOD.");
db.exec("insert into apod (subscribed_room) values (?)", evt.room);
return MSG("Room has been subcribed to APOD!");
} else if (arg.parsed[0] == "unsubscribe") {
if (subscribed) {
db.exec("delete from apod where subscribed_room = ?", evt.room);
return MSG("Room has been unsubscribed from APOD.");
} else return MSG("Room is not subscribed to APOD.");
}
return MSG("Unknown argument");
}

51
source/commands/misc.d Normal file
View File

@ -0,0 +1,51 @@
module commands.misc;
import util, api_data;
import commands.util;
@Command("хз", "хуй")
string[2] huy = [":orehussmile:",
`<img data-mx-emoticon height="32" alt=":orehussmile:" title=":orehussmile:"
src="mxc://4d2.org/XvWYAuhASYRHtYvtspsrWvtU" >`];
@Command("Версия бота", "версия")
string ver = "Neptune Alpha 1.0 :: https://git.macaw.me/skunky/neptune";
@Command("компвер")
static string compver = "Compiler version: "~intToStr(__VERSION__);
@Command("Измеряет пинг")
auto ping(Arguments, EventWithoutRoomID* evt) {
static enum Time: int {
millisecond = 1,
second = 1000,
minute = 60 * second,
hour = 60 * minute,
day = 24 * hour,
week = 7 * day,
month = 4 * week,
year = 12 * month
}
import std.datetime: Clock, SysTime, unixTimeToStdTime;
auto delay = (Clock.currTime() - SysTime(unixTimeToStdTime(0))).total!"msecs" - evt.origin_server_ts;
static foreach_reverse(unit; __traits(allMembers,Time))
if (delay >= __traits(getMember, Time, unit)) // реализовать функцию floatToStr
return MSG("PONG [" ~ intToStr(delay / __traits(getMember, Time, unit)) ~ ' ' ~ unit ~ "s]");
return MSG("шота пошло не так");
}
@Command("хз мне лень делать описание")
auto echo(Arguments argz, EventWithoutRoomID* evt) {
return MSG((argz.raw.length > 6) ? argz.raw[6..$] : "Too small MSG");
}
// @Command("Отображает аватар пользователя")
// auto avatar(Arguments argz, EventWithoutRoomID* evt) {
// string url = cast (string) mkrqst(homeserver~"/_matrix/client/v3/profile/" ~
// ((argz.parsed.length == 0) ? evt.sender : argz.parsed[0]) ~ "/avatar_url").body;
// if (url == "{}") return MSG("User has no avatar");
// return MSG(evt.sender, `<img src="`~url[15..$-2]~`">`);
// }

View File

@ -0,0 +1,128 @@
module commands.moderation;
import util, api_data, api;
import asdf;
import core.stdc.time: time;
import commands.util;
import main: db;
enum TimeErr: int {
@("Not enough arguments") NotEnoughArguments = -1,
@("Invalid date") InvalidDate = -2,
@("Invalid unit") InvalidUnit = -3
}
int parseTime(string str) @nogc pure {
if (str.length < 2) return TimeErr.NotEnoughArguments;
foreach (short c; str[0..$-1])
if (!(c >= 48 && c <= 57))
return TimeErr.InvalidDate;
static foreach(unit; __traits(allMembers, Time))
if (str[$-1] == __traits(getAttributes, __traits(getMember, Time, unit))[0])
return strToInt(str[0..$-1]) * __traits(getMember, Time, unit);
return TimeErr.InvalidUnit;
}
@Command("Admin tools")
MSG nctl(Arguments arg, EventWithoutRoomID* evt) {
if (arg.parsed.length != 2) return MSG("❌️ Not enough arguments");
if (getUsrPL(evt.room, evt.sender) < 50) return MSG("❌️ Unauthorized");
db.exec("create table if not exists nctl (room string, user string, nc string, type string, unt integer)");
bool wd, err;
string cntnt, type;
int dur;
auto durStr = arg.getArg("D");
if (durStr) dur = parseTime(durStr);
if (dur < 0) {
static foreach(Terr; __traits(allMembers, TimeErr)) {
if (dur == __traits(getMember, TimeErr, Terr)) {
return MSG("💥 " ~ __traits(getAttributes, __traits(getMember, TimeErr, Terr))[0]);
}
}
}
void mrq(string ep) {
short rq = mkhsrq(
"/_matrix/client/v3/rooms/"~evt.room~'/'~ep,
"POST",
Moderate(arg.parsed[1], "Moderated by "~evt.sender).serializeToJson
).status;
if (rq != 200) err = true;
}
void setpl(short pl) {
auto pls = State.PowerLevels(getUsrsPLs(evt.room));
if (dur) cntnt = pls.serializeToJson;
pls.users[arg.parsed[1]] = pl;
state(evt.room, "m.room.power_levels", pls);
}
bool c() {
if (db.query("select type from nctl where room = ? and user = ?",evt.room, arg.parsed[1]).step)
return true;
return false;
}
switch (arg.parsed[0]) {
case "ban":
wd = true;
mrq("ban");
break;
case "kick", "unban":
mrq(arg.parsed[0]);
break;
case "mute":
if(c) goto e;
type = "m.room.power_levels";
wd = true;
setpl(-100);
break;
case "unmute": setpl(0); break;
case "acl", "delete-acl":
State.ACL acls = getState!(State.ACL)(evt.room, "m.room.server_acl");
if (dur) cntnt = acls.serializeToJson;
if (arg.parsed[0] == "acl") {
if(c) goto e;
type = "m.room.server_acl";
wd = true;
acls.deny ~= arg.parsed[1];
} else {
foreach (n, srv; acls.deny) {
if (srv == arg.parsed[1])
acls.deny = acls.deny[0..n] ~ acls.deny[n+1..$];
}
}
state(evt.room, "m.room.server_acl", acls);
break;
default: return MSG("❓️ Invalid argument");
}
if (err) return MSG("💥 Something went wrong");
if (dur && wd) {
import std.datetime.systime: SysTime;
auto untill = time(null) + dur;
db.exec(
"insert into nctl (room, user, nc, type, unt) values (?,?,?,?,?)",
evt.room,
arg.parsed[1],
cntnt,
type,
untill
);
return MSG("✅️ Blocked untill " ~ SysTime.fromUnixTime(untill).toSimpleString);
}
return MSG("✅️");
e: return MSG("💥 Already moderated!");
}

54
source/commands/mozhi.d Normal file
View File

@ -0,0 +1,54 @@
module commands.mozhi;
// import api;
// import util;
// import commands.util;
// import api_data;
// import asdf;
// @Command("Translates text via Mozhi")
// auto tr(Arguments argz, EventWithoutRoomID* evt) {
// struct RqRsp {
// string engine;
// string detected;
// @serdeKeys("translated-text") string translatedText;
// string source_language;
// string target_language;
// }
// MSG msg;
// string text;
// auto ngn = argz.getArg("E");
// auto instance = argz.getArg("I");
// MSG origin = evt.content.raw.deserialize!MSG;
// if (argz.parsed.length == 2) {
// auto reply = event(origin.relates.reply.event_id, evt.room).body;
// foreach (chr; reply) {
// if (chr == ' ') text ~= '+';
// else text ~= chr;
// }
// } else for (short i = 2; i < argz.parsed.length; ++i)
// text ~= argz.parsed[i] ~ '+';
// auto rq = mkrqst (
// "https://" ~ (instance ? instance : "mozhi.gitro.xyz") ~ "/api/translate", "POST",
// "engine=" ~ (ngn ? ngn : "google")
// ~ "&from=" ~ argz.parsed[0]
// ~ "&to=" ~ argz.parsed[1]
// ~ "&text=" ~ text,
// false
// );
// if (rq.status != 200) return MSG(intToStr(rq.status));
// auto content = rq.body.deserialize!RqRsp;
// msg = MSG("ЙАЗЫКНЙЭВЫ|БРАН", content.translatedText);
// return msg;
// }
// @Command("Creates audio with text, using Mozhi")
// auto tts(Arguments argz, EventWithoutRoomID* evt) {
// }

81
source/commands/slaves.d Normal file
View File

@ -0,0 +1,81 @@
module commands.slaves;
enum Parallel;
import util;
import api;
import api_data;
import main: db;
void apod_slave() @Parallel {
import core.thread;
import commands.apod;
import core.stdc.time: time;
db.exec("create table if not exists apod (
subscribed_room string,
mxc string unique,
body string,
title string,
mimetype string,
size integer,
width integer,
height integer,
untill integer
)");
for (;;) {
int v;
auto q = db.query("select untill from apod where subscribed_room = 'CACHE'");
if (q.step) v = q.get!int;
for (q = db.query("select subscribed_room from apod where subscribed_room != 'CACHE'"); q.step;) {
if (v == 0 || v < time(null)) {
send(cached_apod, q.get!string);
db.exec("delete from apod where subscribed_room = 'CACHE'");
}
}
Thread.sleep(30.seconds);
}
}
void nctl_slave() @Parallel {
import core.thread: Thread;
import core.time: Duration, dur;
import core.stdc.time: time;
import asdf: serializeToJson;
static Duration tms = dur!"msecs"(10);
struct udr {
string room;
string user;
string type;
string nc;
int unt;
}
for (;;) {
auto q = db.query("select * from nctl");
while (q.step) {
try {
udr z = q.get!udr;
if (time(null) >= z.unt) {
if (z.type == null) {
mkhsrq(
"/_matrix/client/v3/rooms/"~z.room~"/unban",
"POST",
Moderate(z.user, "Its time to unban!").serializeToJson
);
}
else rawState(z.room, z.type, z.nc);
db.exec("delete from nctl where user = ? and room = ?", z.user, z.room);
}
} catch (Exception e) {
import core.stdc.stdio;
printf("[nctl_slave] ERROR: %s\n", cast(char*)e.msg);
}
}
Thread.sleep(tms);
}
}

92
source/commands/util.d Normal file
View File

@ -0,0 +1,92 @@
module commands.util;
struct Command {
string description = "No description provided";
string name;
}
static enum Time: int {
@('s') second = 1,
@('m') minute = 60 * second,
@('h') hour = 60 * minute,
@('d') day = 24 * hour,
@('w') week = 7 * day,
@('M') month = 30 * day,
@('y') year = 12 * month
}
struct Arguments {
string raw;
string command;
string[] parsed;
char[] options;
string getArg(string arg) @nogc {
int prev, splt;
for (int i; i < options.length; ++i) {
if (options[i] == '\0') splt = i;
else if (options[i] == '\1') {
if (arg == options[prev..splt])
return cast(string)options[splt+1..i];
prev = i + 1;
}
}
return null;
}
}
auto parseMsg(string cmd) {
import std.stdio;
Arguments argz = Arguments(cmd);
if (argz.raw[0] == '#') {
string buf;
for (ulong i = 1; i < argz.raw.length; ++i)
if (argz.raw[i] != ' ' && argz.raw[i] != '=') {
buf ~= argz.raw[i];
if (i+1 == argz.raw.length || (argz.raw[i+1] == ' ' || argz.raw[i+1] == '=')) {
argz.parsed ~= buf;
buf = null;
}
}
argz.command = argz.parsed[0]; argz.parsed = argz.parsed[1..$];
for (ulong i; i < argz.parsed.length; ++i)
if (argz.parsed[i][0] == '-') {
string key = argz.parsed[i];
auto val = (i+1 < argz.parsed.length && argz.parsed[i+1][0] != '-')
? argz.parsed[i+1] : null;
if (key[1] == '-')
argz.options ~= key[2..$] ~ '\0' ~ val ~ '\1';
else for (ulong x = 1; x < key.length; ++x)
argz.options ~= key[x..x+1] ~ '\0' ~ val ~ '\1';
auto x = (val) ? i + 2 : i + 1;
argz.parsed = argz.parsed[0..i] ~ argz.parsed[x..$];
i = i - (x - i);
}
}
return argz;
}
int getUsrPL(string room, string user) {
import main: db;
auto q = db.query("select pl from room_pls where room = ? and user = ?", room, user);
if (q.step) return q.get!int;
return 0;
}
short[string] getUsrsPLs(string room) {
struct hz {
string user;
short pl;
}
import main: db;
short[string] buf;
auto q = db.query("select user, pl from room_pls where room = ?", room);
while (q.step) {
auto x = q.get!hz;
buf[x.user] = x.pl;
}
return buf;
}

114
source/listener.d Normal file
View File

@ -0,0 +1,114 @@
module listener;
import api, api_data;
import commands.util;
import main: db;
import util;
import std.stdio: writeln;
import core.thread;
import asdf: deserialize;
__gshared string syncUrl, initialBatch;
void listen(Modules...)() {
static MSG helpgen() {
MSG msg;
static foreach (mod; Modules) {
static foreach (member; __traits(allMembers, mod)) {
static foreach (attr; __traits(getAttributes, __traits(getMember, mod, member))) {
static if (is(typeof(attr) == Command)) msg.body ~= member ~ ": " ~ attr.description ~ '\n';
}
}
}
return msg;
}
auto content = Sync();
content.next_batch = initialBatch;
for (;;) {
string bthUrl = syncUrl ~ content.next_batch;
try {
content = mkhsrq(bthUrl).body.deserialize!Sync;
db.exec("update client set latest_batch = ?", content.next_batch);
} catch (Exception e) { writeln(e.msg); continue; }
// https://github.com/trikko/serverino/blob/3e3e6273a5aaa32615ae9d007a1a83877bc23ab4/source/serverino/worker.d#L168
new Thread(delegate {
foreach (room, _; content.rooms.invite) {
writeln("Joining to room: ", room);
mkhsrq("/_matrix/client/v3/rooms/"~room~"/join", "POST", "{}");
send(MSG("Йа криведко"), room);
fetchMembers(room);
}
foreach (room, roomContent; content.rooms.join) {
foreach(event; roomContent.timeline.events) {
if (event.type == "m.room.message") {
try {
auto evt = deserialize!MSG(event.content.raw);
if (!evt.body.length) break;
event.room = room;
auto argz = parseMsg(evt.body);
if (argz.command == "hlp") {
auto h = helpgen;
h.relates.reply.event_id = event.event_id;
send(h, room);
break;
}
static foreach (mod; Modules) {
static foreach (member; __traits(allMembers, mod)) {
// if (argz.command == member) {
foreach (attr; __traits(getAttributes, __traits(getMember, mod, member))) {
if (is(typeof(attr) == Command)
&& (argz.command == member
|| (argz.command == attr.name && attr.name != null)))
{
alias command = __traits(getMember, mod, member);
static if (is(typeof(command) == function))
auto content = command(argz, &event);
else static if (__traits(isStaticArray, command))
auto content = MSG(command[0], command[1]);
else auto content = MSG(command);
content.relates.reply.event_id = event.event_id;
send(content, room);
}
}
// }
}
}
} catch (Exception e) {
writeln(e.msg);
send(MSG(e.msg), room);
}
}
else if (event.type == "m.room.power_levels") {
auto pls = deserialize!(State.PowerLevels)(event.content.raw);
foreach (user, pl; pls.users) {
auto q = db.query("select pl from room_pls where room = ? and user = ?", room, user);
if (q.step) {
if (q.get!int != pl)
db.exec("update room_pls set pl = ? where room = ? and user = ?", pl, room, user);
} else db.exec("insert into room_pls (room, user, pl) values (?, ?, ?)", room, user, pl);
}
}
// else if (event.type == "m.room.encrypted") {
// import encryption;
// auto e = deserialize!Encrypted(event.content.raw);
// decrypt(&e);
// }
}
}
}).start;
}
}
void fetchMembers(string room) {
auto state = getState!(State.PowerLevels)(room, "m.room.power_levels");
foreach (user, pl; state.users) {
db.exec("insert into room_pls (room, user, pl) values (?, ?, ?)", room, user, pl);
}
}

View File

@ -1,14 +1,37 @@
module util;
import asdf;
string homeserver;
__gshared Config cfg;
struct Config {
string token, homeserver;
string database_path;
bool accept_invitations;
}
string intToStr(T)(T num) {
if (num == 0) return "0";
char[] buf;
bool minus = num < 0;
if (minus) num /= -1;
for(short i; num > 0; ++i) {
buf = (num % 10 + '0')~buf;
num /= 10;
} return cast(string)buf;
}
return cast(string)(minus ? '-'~buf : buf);
}
int strToInt(T)(T str) @nogc {
int num;
bool minus;
for (short i; str.length > i; ++i) {
if (str[i] == '-') minus = true;
else if (str[i] >= '0' || str[i] <= '9')
num = num * 10 + (str[i] - 48);
else break;
}
return minus ? num / -1 : num;
}
struct JsonObject {
@ -20,102 +43,111 @@ struct JsonObject {
return null;
}
void serialize(S)(ref S serializer) {
try serializer.sink.putSmallEscaped(data.parseJson.to!string);
catch (Exception) serializer.sink.putSmallEscaped("{}");
// void serialize(S)(ref S serializer) {
// try serializer.sink.putSmallEscaped(data.parseJson.to!string);
// catch (Exception) serializer.sink.putSmallEscaped("{}");
// }
}
struct RqRsp {
short status;
char[] body;
char[] headers;
string getHeader(string header) @nogc {
int prev, splt;
for (int i; i < headers.length; ++i) {
if (headers[i] == '\0') splt = i;
else if (headers[i] == '\1') {
if (header == headers[prev..splt])
return cast(string)headers[splt+2..i];
prev = i + 1;
}
}
return null;
}
}
struct EventWithoutRoomID {
JsonObject content;
string event_id;
ulong origin_server_ts;
@serdeOptional string sender, state_key, type;
// Unsigned unsigned;
size_t wrt(ubyte[]* outptt, size_t size, size_t nmemb, ubyte* inptt) {
auto len = size * nmemb;
*outptt ~= inptt[0..len];
return len;
}
struct Sync {
struct StrippedStateEvent {
JsonObject content;
string sender, state_key, type;
// struct MM {
// char* rsp;
// size_t len;
// }
// size_t wrt(MM* outptt, size_t size, size_t nmemb, char* inptt) @nogc {
// import core.stdc.stdlib;
// import core.stdc.string;
// size_t rs = size * nmemb;
// outptt.rsp = cast(char*)realloc(outptt.rsp, outptt.len+rs+1);
// memcpy(&(outptt.rsp[outptt.len]), inptt, rs);
// outptt.len += rs;
// outptt.rsp[outptt.len] = '\0';
// return rs;
// }
size_t hddrz(ubyte[]* outptt, size_t size, size_t nmemb, ubyte* inptt) {
auto len = size * nmemb;
auto headers = inptt[0..len];
int prev;
for (int i; i < len; ++i) {
if (headers[i] == '\r' && headers[i + 1] == '\n') {
auto line = headers[prev..i];
prev = i + 1;
for (int x; x < line.length; ++x)
if (line[x] == ':') {
*outptt ~= line[0..x] ~ '\0' ~ line[x + 1..$] ~ '\1';
break;
}
}
}
return len;
}
struct Unsigned {
int age;
string membership, transaction_id;
import bindings.curl;
const char* UA = "Neptune/1.0 (https://code.lost-skunk.cc/neptune)";
// FIXME: memory corruption
RqRsp mkrqst(string url, string method = "GET", string content = null, bool authorized = false) {
RqRsp rsp;
auto hndl = curl_easy_init();
curl_easy_setopt(hndl, CURLOPT_URL, cast(char*)url);
curl_easy_setopt(hndl, CURLOPT_WRITEFUNCTION, &wrt);
curl_easy_setopt(hndl, CURLOPT_WRITEDATA, &rsp.body);
curl_easy_setopt(hndl, CURLOPT_USERAGENT, UA);
curl_easy_setopt(hndl, CURLOPT_HEADERFUNCTION, &hddrz);
curl_easy_setopt(hndl, CURLOPT_HEADERDATA, &rsp.headers);
if (method) curl_easy_setopt(hndl, CURLOPT_CUSTOMREQUEST, cast(char*)method);
if (content) {
curl_easy_setopt(hndl, CURLOPT_POST, 1);
curl_easy_setopt(hndl, CURLOPT_POSTFIELDS, cast(void*)content);
curl_easy_setopt(hndl, CURLOPT_POSTFIELDSIZE, content.length);
}
struct Rooms {
struct Invited {
struct IS { StrippedStateEvent[] events; }
IS invite_state;
}
struct Joined {
struct Timeline {
EventWithoutRoomID[] events;
bool limited;
string prev_batch;
curl_slist* authhdr;
if (authorized) {
authhdr = curl_slist_append(null, cast(char*)cfg.token);
curl_easy_setopt(hndl, CURLOPT_HTTPHEADER, authhdr);
}
struct UNC {
int highlight_count;
int notification_count;
int rs = curl_easy_perform(hndl);
if (rs != 0) {
static foreach (err; __traits(allMembers, CURLcode)) {
if (rs == __traits(getMember, CURLcode, err))
throw new Exception(err);
}
}
curl_easy_getinfo(hndl, CURLINFO_RESPONSE_CODE, &rsp.status);
if (authorized) curl_slist_free_all(authhdr);
curl_easy_cleanup(hndl);
return rsp;
}
@serdeOptional Timeline timeline;
UNC unread_notifications;
}
@serdeOptional Invited[string] invite;
@serdeOptional Joined[string] join;
}
@serdeOptional Rooms rooms;
string next_batch;
}
// EVENTS
struct MSG {
string body;
@serdeOptional @serdeIgnoreDefault string formatted_body;
string msgtype = "m.notice";
@serdeOptional string format = "org.matrix.custom.html";
}
struct Arguments {
string raw;
string command;
string[] parsed;
string[string] options;
}
auto parseMsg(string cmd) {
Arguments argz = Arguments(cmd);
if (argz.raw[0] == '#') {
string buf;
for (ulong i = 1; i < argz.raw.length; ++i)
if (argz.raw[i] != ' ' && argz.raw[i] != '=') {
buf ~= argz.raw[i];
if (i+1 == argz.raw.length || (argz.raw[i+1] == ' ' || argz.raw[i+1] == '=')) {
argz.parsed ~= buf;
buf = null;
}
}
argz.command = argz.parsed[0]; argz.parsed = argz.parsed[1..$];
for (ulong i; i < argz.parsed.length; ++i)
if (argz.parsed[i][0] == '-') {
string key = argz.parsed[i];
auto val = (i+1 < argz.parsed.length && argz.parsed[i+1][0] != '-')
? argz.parsed[i+1] : null;
if (key[1] == '-') argz.options[key[2..$]] = val;
else for (ulong x = 1; x < key.length; ++x)
argz.options[key[x..x+1]] = val;
auto x = (val) ? i + 2 : i + 1;
argz.parsed = argz.parsed[0..i] ~ argz.parsed[x..$];
i = i - (x - i);
}
}
return argz;
RqRsp mkhsrq(string uri, string method = "GET", string content = null) {
return mkrqst(cfg.homeserver ~ uri, method, content, true);
}