Commit c91d0a67 authored by kaniini's avatar kaniini

strip down to base AP code

parent 5e9b2cd2
# this is the path that the object graph will get dumped to (in JSON-LD format),
# you probably shouldn't change it, but you can if you want.
db: relay.jsonld
# this section is for ActivityPub
ap:
# this is used for generating activitypub messages, as well as instructions for
# linking AP identities. it should be an SSL-enabled domain reachable by https.
host: 'relay.pleroma.site'
......@@ -8,7 +8,7 @@ import yaml
def load_config():
with open('viera.yaml') as f:
with open('relay.yaml') as f:
return yaml.load(f)
......
......@@ -43,7 +43,8 @@ async def actor(request):
"followers": "https://{}/followers".format(request.host),
"following": "https://{}/following".format(request.host),
"inbox": "https://{}/inbox".format(request.host),
"name": "Viera",
"sharedInbox": "https://{}/inbox".format(request.host),
"name": "ActivityRelay",
"type": "Application",
"id": "https://{}/actor".format(request.host),
"publicKey": {
......@@ -51,8 +52,8 @@ async def actor(request):
"owner": "https://{}/actor".format(request.host),
"publicKeyPem": DATABASE["actorKeys"]["publicKey"]
},
"summary": "Viera, the bot",
"preferredUsername": "viera",
"summary": "ActivityRelay bot",
"preferredUsername": "relay",
"url": "https://{}/actor".format(request.host)
}
return aiohttp.web.json_response(data)
......@@ -73,7 +74,7 @@ async def push_message_to_actor(actor, message, our_key_id):
'(request-target)': 'post {}'.format(url.path),
'Content-Length': str(len(data)),
'Content-Type': 'application/activity+json',
'User-Agent': 'Viera'
'User-Agent': 'ActivityRelay'
}
headers['signature'] = sign_headers(headers, PRIVKEY, our_key_id)
......@@ -128,28 +129,8 @@ def strip_html(data):
return cgi.escape(no_tags)
from .authreqs import check_reqs, get_irc_bot
async def handle_create(actor, data, request):
# for now, we only care about Notes
if data['object']['type'] != 'Note':
return
# strip the HTML if present
content = strip_html(data['object']['content']).split()
# check if the message is an authorization token for linking identities together
# if it is, then it's not a message we want to relay to IRC.
if check_reqs(content, actor):
return
# check that the message is public before relaying
public_uri = 'https://www.w3.org/ns/activitystreams#Public'
if public_uri in data.get('to', []) or public_uri in data.get('cc', []):
bot = get_irc_bot()
bot.relay_message(actor, data['object'], ' '.join(content))
pass
async def handle_follow(actor, data, request):
......
......@@ -5,7 +5,7 @@ from . import app
async def webfinger(request):
subject = request.query['resource']
if subject != 'acct:viera@{}'.format(request.host):
if subject != 'acct:relay@{}'.format(request.host):
return aiohttp.web.json_response({'error': 'user not found'}, status=404)
actor_uri = "https://{}/actor".format(request.host)
......
# this is the path that the object graph will get dumped to (in JSON-LD format),
# you probably shouldn't change it, but you can if you want.
db: viera.jsonld
# this section configures the IRC bot
irc:
# hostname of IRC network to connect to
host: chat.freenode.net
# port of IRC network to connect to
port: 6697
# whether to use SSL/TLS to connect or not
ssl: true
# the main nickname of the bot to use
nickname: viera
# the username of the bot to use
username: viera
# the realname / GECOS of the bot to use
realname: Viera; https://viera.dereferenced.org
# channels for the bot to join
channels:
- '#mychannel'
- '#myotherchannel'
# channels for the bot to relay AP posts to
relay_channels:
# allow any AP actor to be relayed
'#mychannel': []
# allow only one AP actor to be relayed
'#myotherchannel':
- 'https://example.org/~alyssa'
# IRC services credentials.
sasl_username: viera
sasl_password: examplepass
# IRC users with accounts linked to these AP identities may administer the bot
# (follow and unfollow commands)
privileged:
- 'https://pleroma.site/users/kaniini'
# this section is for ActivityPub
ap:
# this is used for generating activitypub messages, as well as instructions for
# linking AP identities. it should be an SSL-enabled domain reachable by https.
host: 'viera.dereferenced.org'
import uuid
import logging
from collections import namedtuple
from .database import DATABASE
PendingAuth = namedtuple('PendingAuth', ['irc_nickname', 'irc_account', 'actor'])
AUTHS = DATABASE.get('auths', {})
DATABASE["auths"] = AUTHS
PENDING_AUTHS = {}
IRC_BOT = None
def check_reqs(chunks, actor):
global DATABASE
results = [x in PENDING_AUTHS for x in chunks]
logging.debug('AUTHREQ: chunks: %r, results: %r', chunks, results)
if True in results:
pending_slot = results.index(True)
pending_uuid = chunks[pending_slot]
req = PENDING_AUTHS.pop(pending_uuid)._replace(actor=actor["id"])
logging.debug("IRC BOT: %r, AUTHREQ: %r", IRC_BOT, req)
if IRC_BOT:
IRC_BOT.handle_auth_req(req)
DATABASE["auths"][req.irc_account] = req.actor
return True in results
def new_auth_req(irc_nickname, irc_account):
authid = str(uuid.uuid4())
PENDING_AUTHS[authid] = PendingAuth(irc_nickname, irc_account, None)
return authid
# XXX - utter hackjob
def set_irc_bot(bot):
global IRC_BOT
IRC_BOT = bot
logging.debug("SET IRC BOT TO: %r", bot)
def get_irc_bot():
global IRC_BOT
return IRC_BOT
def check_auth(account):
return account in DATABASE["auths"]
def fetch_auth(account):
if check_auth(account):
return DATABASE["auths"][account]
return None
def drop_auth(account):
DATABASE["auths"].pop(account, None)
This diff is collapsed.
# irc_envelope.py
# Purpose: Conversion of RFC1459 messages to/from native objects.
#
# Copyright (c) 2014, William Pitcock <nenolod@dereferenced.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from pprint import pprint
class RFC1459Message(object):
@classmethod
def from_data(cls, verb, params=None, source=None, tags=None):
o = cls()
o.verb = verb
o.tags = dict()
o.source = None
o.params = list()
if params:
o.params = params
if source:
o.source = source
if tags:
o.tags.update(**tags)
return o
@classmethod
def from_message(cls, message):
if isinstance(message, bytes):
message = message.decode('UTF-8', 'replace')
s = message.split(' ')
tags = None
if s[0].startswith('@'):
tag_str = s[0][1:].split(';')
s = s[1:]
tags = {}
for tag in tag_str:
if '=' in tag:
k, v = tag.split('=', 1)
tags[k] = v
else:
tags[tag] = True
source = None
if s[0].startswith(':'):
source = s[0][1:]
s = s[1:]
verb = s[0].upper()
original_params = s[1:]
params = []
while len(original_params):
# skip multiple spaces in middle of message, as per 1459
if original_params[0] == '' and len(original_params) > 1:
original_params.pop(0)
continue
elif original_params[0].startswith(':'):
arg = ' '.join(original_params)[1:]
params.append(arg)
break
else:
params.append(original_params.pop(0))
return cls.from_data(verb, params, source, tags)
def args_to_message(self):
base = []
for arg in self.params:
casted = str(arg)
if casted and ' ' not in casted and casted[0] != ':':
base.append(casted)
else:
base.append(':' + casted)
break
return ' '.join(base)
def to_message(self):
components = []
if self.tags:
components.append('@' + ';'.join([k + '=' + v for k, v in self.tags.items()]))
if self.source:
components.append(':' + self.source)
components.append(self.verb)
if self.params:
components.append(self.args_to_message())
return ' '.join(components)
def to_event(self):
return "rfc1459 message " + self.verb, self.__dict__
def serialize(self):
return self.__dict__
def __repr__(self):
return '[RFC1459Message: "{0}"]'.format(self.to_message())
def test_rfc1459message():
print('====== PARSER TESTS ======')
print(RFC1459Message.from_message('@foo=bar PRIVMSG kaniini :this is a test message!'))
print(RFC1459Message.from_message('@foo=bar :irc.tortois.es 001 kaniini :Welcome to IRC, kaniini!'))
print(RFC1459Message.from_message('PRIVMSG kaniini :this is a test message!'))
print(RFC1459Message.from_message(':irc.tortois.es 001 kaniini :Welcome to IRC, kaniini!'))
print(RFC1459Message.from_message('CAPAB '))
print('====== STRUCTURE TESTS ======')
m = RFC1459Message.from_message('@foo=bar;bar=baz :irc.tortois.es 001 kaniini :Welcome to IRC, kaniini!')
pprint(m.serialize())
print('====== BUILDER TESTS ======')
data = {
'verb': 'PRIVMSG',
'params': ['kaniini', 'hello world!'],
'source': 'kaniini!~kaniini@localhost',
'tags': {'account-name': 'kaniini'},
}
m = RFC1459Message.from_data(**data)
print(m.to_message())
pprint(m.serialize())
print('====== ALL TESTS: PASSED ======')
if __name__ == '__main__':
test_rfc1459message()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment