kopia lustrzana https://gitlab.com/marnanel/chapeau
298 wiersze
7.6 KiB
Python
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
|