chapeau/kepi/bowler_pub/models/actor.py

298 wiersze
7.6 KiB
Python

from django.db import models
from django.conf import settings
from . import acobject, following, collection
import kepi.bowler_pub.crypto as crypto
from kepi.bowler_pub.utils import configured_url, uri_to_url
import logging
import re
logger = logging.getLogger(name='kepi')
LIST_NAMES = [
'inbox', 'outbox', 'followers', 'following',
]
######################
class AcActor(acobject.AcObject):
"""
An AcActor is a kind of AcObject representing a person,
an organisation, a bot, or anything else that can
post stuff and interact with other AcActors.
"""
privateKey = models.TextField(
blank=True,
null=True,
)
f_name = models.TextField(
verbose_name='name',
help_text = 'Your name, in human-friendly form. '+\
'Something like "Alice Liddell".',
)
publicKey = models.TextField(
blank=True,
null=True,
verbose_name='public key',
)
auto_follow = models.BooleanField(
default=True,
help_text="If True, follow requests will be accepted automatically.",
)
f_summary = models.TextField(
max_length=255,
help_text="Your biography. Something like "+\
'"I enjoy falling down rabbitholes."',
default='',
verbose_name='bio',
)
icon = models.ImageField(
help_text="A small square image used to identify you.",
null=True,
verbose_name='icon',
)
header = models.ImageField(
help_text="A large image, wider than it's tall, which appears "+\
"at the top of your profile page.",
null=True,
verbose_name='header image',
)
def _generate_id(self):
return None
def _check_provided_id(self):
if not re.match(r'@[a-z0-9_-]+$', self.id,
re.IGNORECASE):
raise ValueError("Actor IDs begin with an @ "+\
"followed by one or more characters from "+\
"A-Z, a-z, 0-9, underscore, or hyphen. "+\
"You gave: "+self.id)
def _after_create(self):
if self.privateKey is None and self.publicKey is None:
if not self.is_local:
logger.warn('%s: Attempt to save remote without key',
self.url)
else:
logger.info('%s: generating key pair.',
self.url)
key = crypto.Key()
self.privateKey = key.private_as_pem()
self.publicKey = key.public_as_pem()
self.save()
def __str__(self):
if self.is_local:
return '[{}]'.format(
self.id,
)
else:
return '[remote user {}]'.format(
self.id,
)
@property
def key_name(self):
"""
The name of this key.
"""
return '%s#main-key' % (self.url,)
def list_url(self, name):
return configured_url('COLLECTION_LINK',
username = self.id[1:],
listname = name,
)
def __setitem__(self, name, value):
if name=='privateKey':
self.privateKey = value
logger.info('%s: setting private key to %s',
self, self.privateKey)
self.save()
elif name=='publicKey':
from kepi.bowler_pub.utils import as_json
self.publicKey = as_json(value,
indent=None)
logger.info('%s: setting public key to %s',
self, self.publicKey)
self.save()
else:
super().__setitem__(name, value)
@property
def icon_or_default(self):
if self.icon:
return self.icon
which = ord(self.id[1]) % 10
return uri_to_url('/static/defaults/avatar_{}.jpg'.format(
which,
))
@property
def header_or_default(self):
if self.header:
return self.header
return uri_to_url('/static/defaults/header.jpg')
def __getitem__(self, name):
if self.is_local:
if name in LIST_NAMES:
return self.list_url(name)
elif name=='privateKey':
return self.privateKey
elif name in ('preferredUsername', 'username'):
return self.id[1:]
elif name=='url':
return self.url
elif name=='feedURL':
return configured_url('USER_FEED_LINK',
username = self.id[1:],
)
if name=='publicKey':
if not self.publicKey:
logger.debug('%s: we have no known public key',
self)
return None
from json import loads
result = loads(self.publicKey)
logger.debug('%s: public key is %s',
self, result)
return result
result = super().__getitem__(name)
return result
@property
def activity_form(self):
result = super().activity_form
result['publicKey'] = {
'id': self.url + '#main-key',
'owner': self.url,
'publicKeyPem': self.publicKey,
}
for listname in LIST_NAMES:
result[listname] = self.list_url(listname)
for attribute in [
'url',
'preferredUsername',
]:
result[attribute] = self[attribute]
for delendum in [
'published',
]:
del result[delendum]
result['endpoints'] = {
'sharedInbox': configured_url('SHARED_INBOX_LINK'),
}
result['tag'] = []
result['attachment'] = []
result['icon'] = {
"type":"Image",
"mediaType":"image/jpeg",
"url": self['avatar'],
}
result['image'] = {
"type":"Image",
"mediaType":"image/jpeg",
"url": self['header'],
}
return result
@property
def moved_to(self):
return None # TODO
@property
def fields(self):
return [] # FIXME
@property
def emojis(self):
return [] # FIXME
@property
def locked(self):
return False # TODO
@property
def default_visibility(self):
return 'public' # FIXME
@property
def default_sensitive(self):
return False # FIXME
@property
def bot(self):
return False # FIXME
@property
def language(self):
return settings.KEPI['LANGUAGES'][0] # FIXME
@property
def following_count(self):
return following.Following.objects.filter(
follower=self.id,
pending=False).count()
@property
def followers_count(self):
return following.Following.objects.filter(
following=self.id,
pending=False).count()
@property
def statuses_count(self):
return collection.Collection.get(
user = self,
collection = 'outbox',
create_if_missing = True,
).members.count()
class AcApplication(AcActor):
pass
class AcGroup(AcActor):
pass
class AcOrganization(AcActor):
pass
class AcPerson(AcActor):
class Meta:
verbose_name = 'person'
verbose_name_plural = 'people'
class AcService(AcActor):
pass