Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Matias Linares committed Apr 4, 2017
0 parents commit 9c439e6
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
state
file.json
**/*/.precomp
24 changes: 24 additions & 0 deletions META6.json
@@ -0,0 +1,24 @@
{
"perl" : "6.c",
"name" : "Matrix::Client",
"version" : "0.1.0",
"description" : "Simple matrix.org client",
"tags" : [ "Net", "Matrix" ],
"depends" : [
"JSON::Tiny",
"HTTP::UserAgent",
"URI::Encode"
],
"test-depends" : [
"Test",
"Test::META"
],
"provides" : {
"Matrix::Client" : "lib/Matrix/Client.pm6",
"Matrix::Client::Room" : "lib/Matrix/Client/Room.pm6",
"Matrix::Client::Requester" : "lib/Matrix/Client/Requester.pm6",
"Matrix::Client::Common" : "lib/Matrix/Client/Common.pm6"
},
"authors" : ["Matias Linares"],
"support" : {"source" : ""}
}
98 changes: 98 additions & 0 deletions examples/bot.p6
@@ -0,0 +1,98 @@
#!/usr/bin/env perl6
use v6;
use lib "lib";
use JSON::Tiny;
use Matrix::Client;

class Bot {
has $!name = "deprecated";
has $!username is required;
has Bool $!register = False;
has @!room-ids;

has $!on-event;

has Matrix::Client $!client;

submethod BUILD(:$username!, :$password!, :$home-server!, :@room-ids!, :$on-event!) {
$!client = Matrix::Client.new(:home-server($home-server));
$!username = $username;
@!room-ids = @room-ids;
$!on-event = $on-event;

$!client.login($!username, $password);
}

method join-rooms() {
@!room-ids.map: { $!client.join-room($_) }
}

method shutdown() {
$!client.finish;
}

method listen() {
say "Listening";
my $since = "";

loop {
my $sync = { room => timeline => limit => 1 };
my $data = from-json($!client.sync(sync-filter => $sync, since => $since).content);
$since = $data<next_batch>;

for $data<rooms><join>.kv -> $room-id, $d {
for @($d<timeline><events>) -> $ev {
if $ev<type> eq "m.room.message" {
if $ev<content><body>.match($!name) {
my $bot-msg = $!on-event($ev);
if so $bot-msg {
say "Sending message $bot-msg";
my $res = $!client.send($room-id, ~$bot-msg);
if $res.is-success {
say $res.content;
} else {
warn $res.content;
die $res.status-line;
}
}
}
}
}
}
sleep(10);
}
}
}

sub MAIN(Str:D $username, Str:D $password, :$home-server = "https://matrix.deprecated.org") {
my @rooms = "!bpHGYOiCGlvCZarfMH:matrix.deprecated.org";
my $bot = Bot.new:
username => $username,
password => $password,
home-server => $home-server,
room-ids => @rooms,
on-event => -> $ev {
given $ev<content><body> {
when /"say hi"/ {
say "Someone is saying hi!";
"Hello @ {DateTime.now}"
}
default { say "Dunno what's telling me"; Str }
}
};

signal(SIGINT).tap({
$bot.shutdown;
exit 0;
});

my $ress = $bot.join-rooms;
for @($ress) -> $res {
if !$res.is-success {
warn $res.status-line;
warn $res.content;
}
}

$bot.listen;
}
6 changes: 6 additions & 0 deletions examples/rooms.p6
@@ -0,0 +1,6 @@
use Matrix::Client;

my $c = Matrix::Client.new: :home-server<https://matrix.deprecated.org>;
$c.login: @*ARGS[0], @*ARGS[1];

say $c.rooms;
138 changes: 138 additions & 0 deletions lib/Matrix/Client.pm6
@@ -0,0 +1,138 @@
use HTTP::Request::Common;
use URI::Encode;
use JSON::Tiny;
use Matrix::Client::Common;
use Matrix::Client::Room;
use Matrix::Client::Requester;

unit class Matrix::Client does Matrix::Client::Requester;

has Str $!user-id;
has Str $!device-id;
has Str $.state-file = 'state';
has @!rooms;
has @!users;

method user-id() {
$!user-id
}

method device-id() {
$!device-id
}

method login(Str $username, Str $pass) returns Bool {
if $.state-file.IO.e {
my $data = from-json(slurp $.state-file);
$!access-token = $data<access_token>;
$!user-id = $data<user_id>;
$!device-id = $data<device_id>;
$Matrix::Client::Common::TXN-ID = $data<txn_id> // 0;
return True
}

# Handle POST
my $data = to-json {
type => "m.login.password",
user => $username,
password => $pass
};

my $res = $.post("/login", $data);
if $res.is-success {
spurt $.state-file, $res.content;
my $data = from-json($res.content);
$!access-token = $data<access_token>;
$!user-id = $data<user_id>;
$!device-id = $data<device_id>;
True
} else {
False
}
}

method finish() {
my %data =
access_token => $!access-token,
user_id => $!user-id,
device_id => $!device-id,
txn_id => $Matrix::Client::Common::TXN-ID;

spurt $.state-file, to-json(%data);
}

method logout() {
unlink $.state-file;
$.post("/logout")
}

method register($username, $password, Bool :$bind-email? = False) {
my $res = $.post("/register",
username => $username, password => $password,
bind_email => $bind-email,
auth => {
type => "m.login.dummy"
});
if $res.is-success {
my $data = from-json $res.content;
$!access-token = $data<access_token>;
$.user-id = $data<user_id>;
} else {
die "Error with the homeserver: " ~ $res.content;
}
}

method check-res($res) {
if $res.is-success {
True
} else {
warn $res.status-line;
warn $res.content;
False
}
}

multi method sync() {
my $res = $.get("/sync",
timeout => 30000
);

$.check-res($res);
$res
}

multi method sync(Str :$sync-filter, Str :$since = "") {
my $res = $.get("/sync",
timeout => 30000,
filter => $sync-filter,
since => $since
);

$.check-res($res);
$res
}

multi method sync(:$sync-filter is copy, :$since = "") {
$.sync(sync-filter => to-json($sync-filter), since => $since)
}

method join-room($room-id!) {
$.post("/join/" ~ $room-id)
}

method rooms() {
my $res = $.get("/sync", timeout => "30000");

return () unless $res.is-success;
my $data = from-json($res.content);
for $data<rooms><join>.kv -> $id, $json {
@!rooms.push(Matrix::Client::Room.new(id => $id, json => $json, home-server => $!home-server));
}

@!rooms
}

method send(Str $room-id, Str $body, :$type? = "m.text") {
$Matrix::Client::Common::TXN-ID++;
$.put("/rooms/$room-id/send/m.room.message/{$Matrix::Client::Common::TXN-ID}", msgtype => $type, body => $body)
}
3 changes: 3 additions & 0 deletions lib/Matrix/Client/Common.pm6
@@ -0,0 +1,3 @@
unit module Matrix::Client::Common;

our $TXN-ID = 0;
52 changes: 52 additions & 0 deletions lib/Matrix/Client/Requester.pm6
@@ -0,0 +1,52 @@
use HTTP::UserAgent;
use HTTP::Request::Common;
use URI::Encode;
use JSON::Tiny;

unit role Matrix::Client::Requester;

has $!ua = HTTP::UserAgent.new;
has $.home-server is required;
has $!client-endpoint = "/_matrix/client/r0";
has $!url-prefix = "";
has $!access-token = "";
has $!sync-since = "";

method get(Str $path, *%data) {
my $q = "$path?access_token=$!access-token";
for %data.kv -> $k,$v {
$q ~= "&$k=$v" unless $v eq "";
}
my $uri = uri_encode($.base-url ~ $q);

$!ua.history = [];
$!ua.get($uri)
}

method base-url(--> Str) {
"$.home-server$!client-endpoint$!url-prefix"
}

multi method post(Str $path, Str $json) {
my $req = HTTP::Request.new(POST => $.base-url() ~ $path ~ "?access_token=$!access-token",
Content-Type => 'application/json');
$req.add-content($json);
$!ua.history = [];
$!ua.request($req)
}

multi method post(Str $path, *%params) {
self.post($path, to-json(%params))
}

multi method put(Str $path,Str $json) {
my $req = HTTP::Request.new(PUT => $.base-url() ~ $path ~ "?access_token=$!access-token",
Content-Type => 'application/json');
$req.add-content($json);
$!ua.history = [];
$!ua.request($req)
}

multi method put(Str $path, *%params) {
self.put($path, to-json(%params))
}
46 changes: 46 additions & 0 deletions lib/Matrix/Client/Room.pm6
@@ -0,0 +1,46 @@
use JSON::Tiny;
use Matrix::Client::Common;
use Matrix::Client::Requester;

unit class Matrix::Client::Room does Matrix::Client::Requester;

has $.name is rw;
has $.id is rw;
has $!prev-batch;

submethod BUILD(Str :$id!, :$json, :$home-server!) {
$!home-server = $home-server;
$!id = $id;
$!url-prefix = "/rooms/$!id";
$!prev-batch = $json<timeline><prev_batch>;

if so $json {
my @events = $json<state><events>.clone;
for @events -> $ev {
if $ev<type> eq "m.room.name" {
$!name = $ev<content><name>;
}
}
}

# FIXME: Should be a 1:1 conversation
unless $!name {
$!name = "Unknown";
}
}

method messages() {
my $res = $.get("/messages");
my $data = from-json($res.content);

return $data<chunk>.clone;
}

method send($room-id, Str $body!, Str :$type? = "m.text") {
$Matrix::Client::Common::TXN-ID++;
$.put("/send/m.room.message/{$Matrix::Client::Common::TXN-ID}", msgtype => $type, body => $body)
}

method gist(--> Str) {
"Room<name: $.name, id: $.id>"
}
6 changes: 6 additions & 0 deletions t/meta.t
@@ -0,0 +1,6 @@
use lib 'lib';
use Test;
use Test::META;

meta-ok;
done-testing;
7 changes: 7 additions & 0 deletions t/use.t
@@ -0,0 +1,7 @@
use lib 'lib';
use Test;

use-ok 'Matrix::Client';
use-ok 'Matrix::Client::Room';
use-ok 'Matrix::Client::Requester';
done-testing;

0 comments on commit 9c439e6

Please sign in to comment.