slidge 0.2.0a1__py3-none-any.whl → 0.2.0a3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
slidge/__version__.py CHANGED
@@ -2,4 +2,4 @@ from slidge.util.util import get_version # noqa: F401
2
2
 
3
3
  # this is modified before publish, but if someone cloned from the repo,
4
4
  # it can help
5
- __version__ = "0.2.0alpha1"
5
+ __version__ = "0.2.0alpha3"
slidge/contact/contact.py CHANGED
@@ -3,6 +3,7 @@ import logging
3
3
  import warnings
4
4
  from datetime import date
5
5
  from typing import TYPE_CHECKING, Generic, Iterable, Optional, Self, Union
6
+ from xml.etree import ElementTree as ET
6
7
 
7
8
  from slixmpp import JID, Message, Presence
8
9
  from slixmpp.exceptions import IqError
@@ -120,10 +121,21 @@ class LegacyContact(
120
121
  self.xmpp = session.xmpp
121
122
  self.jid = JID(self.jid_username + "@" + self.xmpp.boundjid.bare)
122
123
  self.jid.resource = self.RESOURCE
123
- self.log = logging.getLogger(f"{self.user_jid.bare}:{self.jid.bare}")
124
+ self.log = logging.getLogger(self.jid.bare)
125
+ self._set_logger_name()
124
126
  self._is_friend: bool = False
125
127
  self._added_to_roster = False
126
128
  self._caps_ver: str | None = None
129
+ self._vcard_fetched = False
130
+ self._vcard: str | None = None
131
+
132
+ async def get_vcard(self) -> VCard4 | None:
133
+ if not self._vcard_fetched:
134
+ await self.fetch_vcard()
135
+ if self._vcard is None:
136
+ return None
137
+
138
+ return VCard4(xml=ET.fromstring(self._vcard))
127
139
 
128
140
  @property
129
141
  def is_friend(self):
@@ -136,6 +148,7 @@ class LegacyContact(
136
148
  self._is_friend = value
137
149
  if self._updating_info:
138
150
  return
151
+ self.__ensure_pk()
139
152
  assert self.contact_pk is not None
140
153
  self.xmpp.store.contacts.set_friend(self.contact_pk, value)
141
154
 
@@ -157,7 +170,10 @@ class LegacyContact(
157
170
 
158
171
  @property
159
172
  def participants(self) -> list["LegacyParticipant"]:
160
- assert self.contact_pk is not None
173
+ if self.contact_pk is None:
174
+ return []
175
+
176
+ self.__ensure_pk()
161
177
  from ..group.participant import LegacyParticipant
162
178
 
163
179
  return [
@@ -171,8 +187,25 @@ class LegacyContact(
171
187
  def user_jid(self):
172
188
  return self.session.user_jid
173
189
 
190
+ def _set_logger_name(self):
191
+ self.log.name = f"{self.user_jid.bare}:contact:{self}"
192
+
174
193
  def __repr__(self):
175
- return f"<Contact {self.jid.bare} - {self.name or self.legacy_id}'>"
194
+ return f"<Contact #{self.contact_pk} '{self.name}' ({self.legacy_id} - {self.jid.local})'>"
195
+
196
+ def __ensure_pk(self):
197
+ if self.contact_pk is not None:
198
+ return
199
+ with self.xmpp.store.session() as orm:
200
+ orm.commit()
201
+ stored = self.xmpp.store.contacts.get_by_legacy_id(
202
+ self.user_pk, str(self.legacy_id)
203
+ )
204
+ if stored is None:
205
+ self.log.error("Cannot find our primary key!", stack_info=True)
206
+ raise RuntimeError("Cannot find our primary key!")
207
+ self.contact_pk = stored.id
208
+ assert self.contact_pk is not None
176
209
 
177
210
  def __get_subscription_string(self):
178
211
  if self.is_friend:
@@ -234,6 +267,7 @@ class LegacyContact(
234
267
  and self.xmpp.MARK_ALL_MESSAGES
235
268
  and is_markable(stanza)
236
269
  ):
270
+ self.__ensure_pk()
237
271
  assert self.contact_pk is not None
238
272
  self.xmpp.store.contacts.add_to_sent(self.contact_pk, stanza["id"])
239
273
  stanza["to"] = self.user_jid
@@ -254,6 +288,7 @@ class LegacyContact(
254
288
  :param horizon_xmpp_id: The latest message
255
289
  :return: A list of XMPP ids or None if horizon_xmpp_id was not found
256
290
  """
291
+ self.__ensure_pk()
257
292
  assert self.contact_pk is not None
258
293
  return self.xmpp.store.contacts.pop_sent_up_to(self.contact_pk, horizon_xmpp_id)
259
294
 
@@ -269,6 +304,7 @@ class LegacyContact(
269
304
  if self._name == n:
270
305
  return
271
306
  self._name = n
307
+ self._set_logger_name()
272
308
  if self.is_friend and self.added_to_roster:
273
309
  self.xmpp.pubsub.broadcast_nick(
274
310
  user_jid=self.user_jid, jid=self.jid.bare, nick=n
@@ -279,6 +315,7 @@ class LegacyContact(
279
315
  return
280
316
  for p in self.participants:
281
317
  p.nickname = n
318
+ self.__ensure_pk()
282
319
  assert self.contact_pk is not None
283
320
  self.xmpp.store.contacts.update_nick(self.contact_pk, n)
284
321
 
@@ -293,6 +330,7 @@ class LegacyContact(
293
330
  if self.contact_pk is None:
294
331
  # happens in LegacyRoster.fill(), the contact primary key is not
295
332
  # set yet, but this will eventually be called in LegacyRoster.__finish_init_contact
333
+ self.log.debug("Not setting avatar PK")
296
334
  return
297
335
  self.xmpp.store.contacts.set_avatar(self.contact_pk, self._avatar_pk)
298
336
  for p in self.participants:
@@ -346,7 +384,17 @@ class LegacyContact(
346
384
  elif country:
347
385
  vcard.add_address(country, locality)
348
386
 
349
- self.xmpp.vcard.set_vcard(self.jid.bare, vcard, {self.user_jid.bare})
387
+ self._vcard = str(vcard)
388
+ self._vcard_fetched = True
389
+ self.session.create_task(
390
+ self.xmpp.pubsub.broadcast_vcard_event(self.jid, self.user_jid, vcard)
391
+ )
392
+
393
+ if self._updating_info:
394
+ return
395
+
396
+ assert self.contact_pk is not None
397
+ self.xmpp.store.contacts.set_vcard(self.contact_pk, self._vcard)
350
398
 
351
399
  def get_roster_item(self):
352
400
  item = {
@@ -430,7 +478,7 @@ class LegacyContact(
430
478
  """
431
479
  self.is_friend = True
432
480
  self.added_to_roster = True
433
- assert self.contact_pk is not None
481
+ self.__ensure_pk()
434
482
  self.log.debug("Accepting friend request")
435
483
  presence = self._make_presence(ptype="subscribed", pstatus=text, bare=True)
436
484
  self._send(presence, nick=True)
@@ -554,8 +602,11 @@ class LegacyContact(
554
602
  contact.added_to_roster = stored.added_to_roster
555
603
  if (data := stored.extra_attributes) is not None:
556
604
  contact.deserialize_extra_attributes(data)
557
- contact._set_avatar_from_store(stored)
558
605
  contact._caps_ver = stored.caps_ver
606
+ contact._set_logger_name()
607
+ contact._set_avatar_from_store(stored)
608
+ contact._vcard = stored.vcard
609
+ contact._vcard_fetched = stored.vcard_fetched
559
610
  return contact
560
611
 
561
612
 
@@ -36,7 +36,6 @@ from ...command.register import RegistrationType
36
36
  from ...db import GatewayUser, SlidgeStore
37
37
  from ...db.avatar import avatar_cache
38
38
  from ...slixfix.roster import RosterBackend
39
- from ...slixfix.xep_0292.vcard4 import VCard4Provider
40
39
  from ...util import ABCSubclassableOnceAtMost
41
40
  from ...util.types import AvatarType, MessageOrPresenceTypeVar
42
41
  from ...util.util import timeit
@@ -331,7 +330,6 @@ class BaseGateway(
331
330
 
332
331
  self.register_plugin("pubsub", {"component_name": self.COMPONENT_NAME})
333
332
  self.pubsub: PubSubComponent = self["pubsub"]
334
- self.vcard: VCard4Provider = self["xep_0292_provider"]
335
333
  self.delivery_receipt: DeliveryReceipt = DeliveryReceipt(self)
336
334
 
337
335
  # with this we receive user avatar updates
@@ -68,6 +68,13 @@ class SessionDispatcher:
68
68
  _exceptions_to_xmpp_errors(self.on_ibr_remove), # type: ignore
69
69
  )
70
70
  )
71
+ self.xmpp.register_handler(
72
+ CoroutineCallback(
73
+ "get_vcard",
74
+ StanzaPath("iq@type=get/vcard"),
75
+ _exceptions_to_xmpp_errors(self.on_get_vcard), # type:ignore
76
+ )
77
+ )
71
78
 
72
79
  for event in (
73
80
  "legacy_message",
@@ -778,6 +785,18 @@ class SessionDispatcher:
778
785
 
779
786
  raise XMPPError("feature-not-implemented")
780
787
 
788
+ async def on_get_vcard(self, iq: Iq):
789
+ session = await self.__get_session(iq)
790
+ session.raise_if_not_logged()
791
+ contact = await session.contacts.by_jid(iq.get_to())
792
+ vcard = await contact.get_vcard()
793
+ reply = iq.reply()
794
+ if vcard:
795
+ reply.append(vcard)
796
+ else:
797
+ reply.enable("vcard")
798
+ reply.send()
799
+
781
800
  def _xmpp_msg_id_to_legacy(self, session: "BaseSession", xmpp_id: str):
782
801
  sent = self.xmpp.store.sent.get_legacy_id(session.user_pk, xmpp_id)
783
802
  if sent is not None:
@@ -79,14 +79,14 @@ class VCardTemp:
79
79
  elif not (contact := entity.contact):
80
80
  raise XMPPError("item-not-found", "This participant has no contact")
81
81
  else:
82
- vcard = await self.xmpp.vcard.get_vcard(contact.jid, iq.get_from())
82
+ vcard = await contact.get_vcard()
83
83
  avatar = contact.get_avatar()
84
84
  type_ = "image/png"
85
85
  else:
86
86
  avatar = entity.get_avatar()
87
87
  type_ = "image/png"
88
88
  if isinstance(entity, LegacyContact):
89
- vcard = await self.xmpp.vcard.get_vcard(entity.jid, iq.get_from())
89
+ vcard = await entity.get_vcard()
90
90
  else:
91
91
  vcard = None
92
92
  v = self.xmpp.plugin["xep_0054"].make_vcard()
slidge/core/pubsub.py CHANGED
@@ -135,6 +135,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
135
135
 
136
136
  to = p.get_to()
137
137
 
138
+ contact = None
138
139
  # we don't want to push anything for contacts that are not in the user's roster
139
140
  if to != self.xmpp.boundjid.bare:
140
141
  session = self.xmpp.get_session_from_stanza(p)
@@ -186,17 +187,19 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
186
187
  else:
187
188
  await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
188
189
 
189
- if VCARD4_NAMESPACE + "+notify" in features:
190
- await self.broadcast_vcard_event(p.get_to(), to=from_)
190
+ if contact is not None and VCARD4_NAMESPACE + "+notify" in features:
191
+ await self.broadcast_vcard_event(
192
+ p.get_to(), from_, await contact.get_vcard()
193
+ )
191
194
 
192
- async def broadcast_vcard_event(self, from_, to):
195
+ async def broadcast_vcard_event(self, from_: JID, to: JID, vcard: VCard4 | None):
193
196
  item = Item()
194
197
  item.namespace = VCARD4_NAMESPACE
195
198
  item["id"] = "current"
196
- vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
199
+ # vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
197
200
  # The vcard content should NOT be in this event according to the spec:
198
201
  # https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
199
- # but movim expects it to be here, and I guess
202
+ # but movim expects it to be here, and I guess it does not hurt
200
203
 
201
204
  log.debug("Broadcast vcard4 event: %s", vcard)
202
205
  await self.__broadcast(
@@ -268,10 +271,9 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
268
271
  # this is not the proper way that clients should retrieve VCards, but
269
272
  # gajim does it this way.
270
273
  # https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
271
- vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(
272
- iq.get_to().bare, iq.get_from().bare
273
- )
274
- log.debug("VCARD: %s -- %s -- %s", iq.get_to().bare, iq.get_from().bare, vcard)
274
+ session = self.xmpp.get_session_from_stanza(iq)
275
+ contact = await session.contacts.by_jid(iq.get_to())
276
+ vcard = await contact.get_vcard()
275
277
  if vcard is None:
276
278
  raise XMPPError("item-not-found")
277
279
  self._reply_with_payload(iq, vcard, "current", VCARD4_NAMESPACE)
File without changes
@@ -0,0 +1,44 @@
1
+ """Lift room legacy ID constraint
2
+
3
+ Revision ID: 5bd48bfdffa2
4
+ Revises: b64b1a793483
5
+ Create Date: 2024-07-24 10:29:23.467851
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+
13
+ from slidge.db.models import Room
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "5bd48bfdffa2"
17
+ down_revision: Union[str, None] = "b64b1a793483"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ with op.batch_alter_table(
24
+ "room",
25
+ schema=None,
26
+ # without copy_from, the newly created table keeps the constraints
27
+ # we actually want to ditch.
28
+ copy_from=Room.__table__, # type:ignore
29
+ ) as batch_op:
30
+ batch_op.create_unique_constraint(
31
+ "uq_room_user_account_id_jid", ["user_account_id", "jid"]
32
+ )
33
+ batch_op.create_unique_constraint(
34
+ "uq_room_user_account_id_legacy_id", ["user_account_id", "legacy_id"]
35
+ )
36
+
37
+
38
+ def downgrade() -> None:
39
+ # ### commands auto generated by Alembic - please adjust! ###
40
+ with op.batch_alter_table("room", schema=None) as batch_op:
41
+ batch_op.drop_constraint("uq_room_user_account_id_legacy_id", type_="unique")
42
+ batch_op.drop_constraint("uq_room_user_account_id_jid", type_="unique")
43
+
44
+ # ### end Alembic commands ###
@@ -0,0 +1,43 @@
1
+ """Add vcard content to contact table
2
+
3
+ Revision ID: 8b993243a536
4
+ Revises: 5bd48bfdffa2
5
+ Create Date: 2024-07-24 07:02:47.770894
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "8b993243a536"
16
+ down_revision: Union[str, None] = "5bd48bfdffa2"
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ with op.batch_alter_table("contact", schema=None) as batch_op:
24
+ batch_op.add_column(sa.Column("vcard", sa.String(), nullable=True))
25
+ batch_op.add_column(
26
+ sa.Column(
27
+ "vcard_fetched",
28
+ sa.Boolean(),
29
+ nullable=False,
30
+ server_default=sa.sql.true(),
31
+ )
32
+ )
33
+
34
+ # ### end Alembic commands ###
35
+
36
+
37
+ def downgrade() -> None:
38
+ # ### commands auto generated by Alembic - please adjust! ###
39
+ with op.batch_alter_table("contact", schema=None) as batch_op:
40
+ batch_op.drop_column("vcard_fetched")
41
+ batch_op.drop_column("vcard")
42
+
43
+ # ### end Alembic commands ###
@@ -58,10 +58,19 @@ def downgrade() -> None:
58
58
 
59
59
 
60
60
  def migrate_from_shelf(accounts: sa.Table) -> None:
61
+ from slidge import global_config
62
+
63
+ db_file = global_config.HOME_DIR / "slidge.db"
64
+ if not db_file.exists():
65
+ return
66
+
61
67
  try:
62
- from slidge.util.db import user_store
68
+ from slidge.db.alembic.old_user_store import user_store
63
69
  except ImportError:
64
70
  return
71
+
72
+ user_store.set_file(db_file, global_config.SECRET_KEY)
73
+
65
74
  try:
66
75
  users = list(user_store.get_all())
67
76
  except AttributeError:
@@ -83,3 +92,6 @@ def migrate_from_shelf(accounts: sa.Table) -> None:
83
92
  for user in users
84
93
  ],
85
94
  )
95
+
96
+ user_store.close()
97
+ db_file.unlink()
slidge/db/meta.py CHANGED
@@ -58,6 +58,13 @@ JSONSerializable = dict[str, JSONSerializableTypes]
58
58
 
59
59
  class Base(sa.orm.DeclarativeBase):
60
60
  type_annotation_map = {JSONSerializable: JSONEncodedDict, JID: JIDType}
61
+ naming_convention = {
62
+ "ix": "ix_%(column_0_label)s",
63
+ "uq": "uq_%(table_name)s_%(column_0_name)s",
64
+ "ck": "ck_%(table_name)s_`%(constraint_name)s`",
65
+ "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
66
+ "pk": "pk_%(table_name)s",
67
+ }
61
68
 
62
69
 
63
70
  def get_engine(path: str) -> sa.Engine:
slidge/db/models.py CHANGED
@@ -166,6 +166,9 @@ class Contact(Base):
166
166
  )
167
167
  updated: Mapped[bool] = mapped_column(default=False)
168
168
 
169
+ vcard: Mapped[Optional[str]] = mapped_column()
170
+ vcard_fetched: Mapped[bool] = mapped_column(default=False)
171
+
169
172
  participants: Mapped[list["Participant"]] = relationship(back_populates="contact")
170
173
 
171
174
 
@@ -191,13 +194,20 @@ class Room(Base):
191
194
  Legacy room
192
195
  """
193
196
 
197
+ __table_args__ = (
198
+ UniqueConstraint(
199
+ "user_account_id", "legacy_id", name="uq_room_user_account_id_legacy_id"
200
+ ),
201
+ UniqueConstraint("user_account_id", "jid", name="uq_room_user_account_id_jid"),
202
+ )
203
+
194
204
  __tablename__ = "room"
195
205
  id: Mapped[int] = mapped_column(primary_key=True)
196
206
  user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
197
207
  user: Mapped[GatewayUser] = relationship(back_populates="rooms")
198
- legacy_id: Mapped[str] = mapped_column(unique=True, nullable=False)
208
+ legacy_id: Mapped[str] = mapped_column(nullable=False)
199
209
 
200
- jid: Mapped[JID] = mapped_column(unique=True)
210
+ jid: Mapped[JID] = mapped_column(nullable=False)
201
211
 
202
212
  avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
203
213
  avatar: Mapped[Avatar] = relationship(back_populates="rooms")
slidge/db/store.py CHANGED
@@ -32,6 +32,7 @@ from .models import (
32
32
  XmppIdsMulti,
33
33
  XmppToLegacyEnum,
34
34
  XmppToLegacyIds,
35
+ participant_hats,
35
36
  )
36
37
 
37
38
  if TYPE_CHECKING:
@@ -379,11 +380,22 @@ class ContactStore(UpdatedMixin):
379
380
  row.updated = True
380
381
  row.extra_attributes = contact.serialize_extra_attributes()
381
382
  row.caps_ver = contact._caps_ver
383
+ row.vcard = contact._vcard
384
+ row.vcard_fetched = contact._vcard_fetched
382
385
  session.add(row)
383
386
  if commit:
384
387
  session.commit()
385
388
  return row.id
386
389
 
390
+ def set_vcard(self, contact_pk: int, vcard: str | None) -> None:
391
+ with self.session() as session:
392
+ session.execute(
393
+ update(Contact)
394
+ .where(Contact.id == contact_pk)
395
+ .values(vcard=vcard, vcard_fetched=True)
396
+ )
397
+ session.commit()
398
+
387
399
  def add_to_sent(self, contact_pk: int, msg_id: str) -> None:
388
400
  with self.session() as session:
389
401
  new = ContactSent(contact_id=contact_pk, msg_id=msg_id)
@@ -659,7 +671,7 @@ class MultiStore(EngineMixin):
659
671
  if existing is not None:
660
672
  if fail:
661
673
  raise
662
- log.warning("Resetting multi for %s", legacy_msg_id)
674
+ log.debug("Resetting multi for %s", legacy_msg_id)
663
675
  session.execute(
664
676
  delete(LegacyIdsMulti)
665
677
  .where(LegacyIdsMulti.user_account_id == user_pk)
@@ -935,6 +947,7 @@ class ParticipantStore(EngineMixin):
935
947
  with self.session() as session:
936
948
  session.execute(delete(Participant))
937
949
  session.execute(delete(Hat))
950
+ session.execute(delete(participant_hats))
938
951
  session.commit()
939
952
 
940
953
  def add(self, room_pk: int, nickname: str) -> int:
slidge/group/room.py CHANGED
@@ -131,7 +131,6 @@ class LegacyMUC(
131
131
  def __init__(self, session: "BaseSession", legacy_id: LegacyGroupIdType, jid: JID):
132
132
  self.session = session
133
133
  self.xmpp: "BaseGateway" = session.xmpp
134
- self.log = logging.getLogger(f"{self.user_jid.bare}:muc:{jid}")
135
134
 
136
135
  self.legacy_id = legacy_id
137
136
  self.jid = jid
@@ -156,6 +155,8 @@ class LegacyMUC(
156
155
 
157
156
  self._n_participants: Optional[int] = None
158
157
 
158
+ self.log = logging.getLogger(self.jid.bare)
159
+ self._set_logger_name()
159
160
  super().__init__()
160
161
 
161
162
  @property
@@ -176,8 +177,11 @@ class LegacyMUC(
176
177
  def user_jid(self):
177
178
  return self.session.user_jid
178
179
 
180
+ def _set_logger_name(self):
181
+ self.log = logging.getLogger(f"{self.user_jid}:muc:{self}")
182
+
179
183
  def __repr__(self):
180
- return f"<MUC {self.legacy_id}/{self.jid}/{self.name}>"
184
+ return f"<MUC #{self.pk} '{self.name}' ({self.legacy_id} - {self.jid.local})'>"
181
185
 
182
186
  @property
183
187
  def subject_date(self) -> Optional[datetime]:
@@ -287,6 +291,7 @@ class LegacyMUC(
287
291
  if self.DISCO_NAME == n:
288
292
  return
289
293
  self.DISCO_NAME = n
294
+ self._set_logger_name()
290
295
  self.__send_configuration_change((104,))
291
296
  if self._updating_info:
292
297
  return
@@ -1207,6 +1212,7 @@ class LegacyMUC(
1207
1212
  muc._user_resources = set(json.loads(stored.user_resources))
1208
1213
  muc._subject_setter = stored.subject_setter
1209
1214
  muc.archive = MessageArchive(muc.pk, session.xmpp.store.mam)
1215
+ muc._set_logger_name()
1210
1216
  muc._set_avatar_from_store(stored)
1211
1217
  return muc
1212
1218
 
slidge/main.py CHANGED
@@ -32,7 +32,6 @@ from slidge.db.avatar import avatar_cache
32
32
  from slidge.db.meta import get_engine
33
33
  from slidge.migration import migrate
34
34
  from slidge.util.conf import ConfigModule
35
- from slidge.util.db import user_store
36
35
 
37
36
 
38
37
  class MainConfig(ConfigModule):
@@ -114,9 +113,6 @@ def configure():
114
113
  logging.info("Creating directory '%s'", h)
115
114
  h.mkdir()
116
115
 
117
- db_file = config.HOME_DIR / "slidge.db"
118
- user_store.set_file(db_file, args.secret_key)
119
-
120
116
  config.UPLOAD_REQUESTER = config.UPLOAD_REQUESTER or config.JID.bare
121
117
 
122
118
  return unknown_argv
slidge/slixfix/roster.py CHANGED
@@ -1,9 +1,8 @@
1
+ import logging
1
2
  from typing import TYPE_CHECKING
2
3
 
3
4
  from slixmpp import JID
4
5
 
5
- from ..util.db import log
6
-
7
6
  if TYPE_CHECKING:
8
7
  from .. import BaseGateway
9
8
 
@@ -65,3 +64,6 @@ class RosterBackend:
65
64
  "whitelisted": False,
66
65
  "subscription": "none",
67
66
  }
67
+
68
+
69
+ log = logging.getLogger(__name__)
@@ -1,103 +1,14 @@
1
- import logging
2
- from typing import TYPE_CHECKING, NamedTuple, Optional
3
-
4
- from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
5
1
  from slixmpp.plugins.base import BasePlugin, register_plugin
6
- from slixmpp.plugins.xep_0292.stanza import NS, VCard4
7
- from slixmpp.types import JidStr
8
-
9
- from slidge.contact import LegacyContact
10
-
11
- if TYPE_CHECKING:
12
- from slidge.core.gateway import BaseGateway
13
-
14
-
15
- class StoredVCard(NamedTuple):
16
- content: VCard4
17
- authorized_jids: set[JidStr]
2
+ from slixmpp.plugins.xep_0292.stanza import NS
18
3
 
19
4
 
20
5
  class VCard4Provider(BasePlugin):
21
- xmpp: "BaseGateway"
22
-
23
6
  name = "xep_0292_provider"
24
7
  description = "VCard4 Provider"
25
8
  dependencies = {"xep_0030"}
26
9
 
27
- def __init__(self, *a, **k):
28
- super(VCard4Provider, self).__init__(*a, **k)
29
- # TODO: store that in DB and not in RAM
30
- self._vcards = dict[JidStr, StoredVCard]()
31
-
32
10
  def plugin_init(self):
33
- self.xmpp.register_handler(
34
- CoroutineCallback(
35
- "get_vcard",
36
- StanzaPath(f"iq@type=get/vcard"),
37
- self.handle_vcard_get, # type:ignore
38
- )
39
- )
40
-
41
11
  self.xmpp.plugin["xep_0030"].add_feature(NS)
42
12
 
43
- def _get_cached_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
44
- vcard = self._vcards.get(JID(jid).bare)
45
- if vcard:
46
- if auth := vcard.authorized_jids:
47
- if JID(requested_by).bare in auth:
48
- return vcard.content
49
- else:
50
- return vcard.content
51
- return None
52
-
53
- async def get_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
54
- if vcard := self._get_cached_vcard(jid, requested_by):
55
- log.debug("Found a cached vcard")
56
- return vcard
57
- if not hasattr(self.xmpp, "get_session_from_jid"):
58
- return None
59
- jid = JID(jid)
60
- if not jid.local:
61
- return None
62
- requested_by = JID(requested_by)
63
- session = self.xmpp.get_session_from_jid(requested_by)
64
- if session is None:
65
- return
66
- entity = await session.get_contact_or_group_or_participant(jid)
67
- if isinstance(entity, LegacyContact):
68
- log.debug("Fetching vcard")
69
- await entity.fetch_vcard()
70
- return self._get_cached_vcard(jid, requested_by)
71
- return None
72
-
73
- async def handle_vcard_get(self, iq: Iq):
74
- r = iq.reply()
75
- if vcard := await self.get_vcard(iq.get_to().bare, iq.get_from().bare):
76
- r.append(vcard)
77
- else:
78
- r.enable("vcard")
79
- r.send()
80
-
81
- def set_vcard(
82
- self,
83
- jid: JidStr,
84
- vcard: VCard4,
85
- /,
86
- authorized_jids: Optional[set[JidStr]] = None,
87
- ):
88
- cache = self._vcards.get(jid)
89
- new = StoredVCard(
90
- vcard, authorized_jids if authorized_jids is not None else set()
91
- )
92
- self._vcards[jid] = new
93
- if cache == new:
94
- return
95
- if self.xmpp["pubsub"] and authorized_jids:
96
- for to in authorized_jids:
97
- self.xmpp.loop.create_task(
98
- self.xmpp["pubsub"].broadcast_vcard_event(jid, to)
99
- )
100
-
101
13
 
102
14
  register_plugin(VCard4Provider)
103
- log = logging.getLogger(__name__)
slidge/util/test.py CHANGED
@@ -287,7 +287,7 @@ class SlidgeTest(SlixTestPlus):
287
287
  stanza = self.next_sent()
288
288
  assert "yup" in stanza["status"].lower(), stanza
289
289
 
290
- self.romeo = BaseSession.get_self_or_unique_subclass().from_jid(
290
+ self.romeo: BaseSession = BaseSession.get_self_or_unique_subclass().from_jid(
291
291
  JID("romeo@montague.lit")
292
292
  )
293
293
  self.juliet: LegacyContact = self.run_coro(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: slidge
3
- Version: 0.2.0a1
3
+ Version: 0.2.0a3
4
4
  Summary: XMPP bridging framework
5
5
  Home-page: https://sr.ht/~nicoco/slidge/
6
6
  License: AGPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  slidge/__init__.py,sha256=S0tUjqpZlzsr8G4Y_1Xt-KCYB07qaknTB0OwHU8k29U,1587
2
2
  slidge/__main__.py,sha256=Y12eh1TD_C5MB50KgEAuMffGnRFCvKYFKHD4UYSmHA0,72
3
- slidge/__version__.py,sha256=I-T5nrkdyqNFMxbn8y7FgSsLGaj0eM2dLL9f5LDsI2Q,170
3
+ slidge/__version__.py,sha256=Ip83rsG7PNyMNP7tUYp3yfTqTCEX_G49W_HLxj1iJ7U,170
4
4
  slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
5
5
  slidge/command/adhoc.py,sha256=5xLLoWyUJqCJB7kAwD2VKYTl_7MObCsgdz-qg_WkWGs,9417
6
6
  slidge/command/admin.py,sha256=JAN8s-b2KckgUozAQNBGOzkrEaQv-LtwezYgOsAR6Ng,5768
@@ -10,12 +10,12 @@ slidge/command/chat_command.py,sha256=kMnxrzmD7LhWgyO1w9Rgz1eA7PhfAJ2Rf34YWjoKrw
10
10
  slidge/command/register.py,sha256=fzPcGUoJtainnDOiC13gWV-uYLuJcsmdKGJ-jXT1qIo,6697
11
11
  slidge/command/user.py,sha256=nJnoU4t1r9oPkFFHSHHwSY8WOQNLQn9mMcwZp-yZaKA,11422
12
12
  slidge/contact/__init__.py,sha256=WMMaHk7UW7YT9EH2LtPdkU0bHQaOp4ikBhbBQskmoc8,191
13
- slidge/contact/contact.py,sha256=Ag4Gq81jlEsz8MMXyU_VcLGJY7_IUgY76jOMlmAQpJc,20237
13
+ slidge/contact/contact.py,sha256=l0zolnfrcSdS7z49TI-CSzjQiqJ-ARSMnZuszBcILXk,21894
14
14
  slidge/contact/roster.py,sha256=2EGV7q0km6JEahn2brr-td0KJILtXQGFWiSPuORA86I,10756
15
15
  slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
16
16
  slidge/core/config.py,sha256=leNcN_TI0Ka1hhzOHx7cBW3fNj5xZwsiv9l8AfRY_vU,7630
17
17
  slidge/core/gateway/__init__.py,sha256=rZckY2gAE-mon77_DSsAW1XtWqhBAETE2d4FqZ8pJXk,58
18
- slidge/core/gateway/base.py,sha256=V-9HMokUfeTpIO_dqU1ONDMXnNMBCEJJbmob-oYjD8s,38600
18
+ slidge/core/gateway/base.py,sha256=6AOWuJPC4GkLcsgvrxHT5Y86g2x6Kv0iMbkp2wjepxo,38483
19
19
  slidge/core/gateway/caps.py,sha256=jemB4tB_2MTAxqQw5Bs4b7qNQ8gLPuAve0aoH6TzLEs,1937
20
20
  slidge/core/gateway/delivery_receipt.py,sha256=AT_9gvZrtWpSRsDJcYjE8CmF7TW-YBbUPdqNW5zWAdo,1352
21
21
  slidge/core/gateway/disco.py,sha256=uazgDXSDb5KrardjPCvElItARcxkeBRohtx82A2BlCQ,2203
@@ -25,8 +25,8 @@ slidge/core/gateway/ping.py,sha256=_zzPkjqvxjTxLNP1jbj0WVLMaybxbYqrKDRM5jHSDjs,1
25
25
  slidge/core/gateway/presence.py,sha256=Ls8IY4uNQaW8F3F1CpRhfyFIVbd_py_VkZyJKMMei8s,2732
26
26
  slidge/core/gateway/registration.py,sha256=JXwIQ-QqZCPXEmCU2G8FvIYDGvD8L8CqGb_Qkbycgt0,2303
27
27
  slidge/core/gateway/search.py,sha256=08ds6gvzX3EnTH-AU8X8J8JKEKYaSrRGTFwwClTT-Rc,3495
28
- slidge/core/gateway/session_dispatcher.py,sha256=mV3ds7zulD1_ivaV-QfilRKk2aN8T0XgcicCYOiYfx0,32306
29
- slidge/core/gateway/vcard_temp.py,sha256=5Trs0O240mpOoFeRzprAYX86W7Qaq9hFhi06437w0Yg,4694
28
+ slidge/core/gateway/session_dispatcher.py,sha256=F8YGUCB7hA1PNsGgQDaUYHmLnf4e8NWptIh-9ra_43c,32930
29
+ slidge/core/gateway/vcard_temp.py,sha256=2FeP-comBg8A-OtB-II1jlxyDib_C4R8vfnJHGNkzyc,4626
30
30
  slidge/core/mixins/__init__.py,sha256=muReAzgvENgMvlfm0Fpe6BQFfm2EMjoDe9ZhGgo6Vig,627
31
31
  slidge/core/mixins/attachment.py,sha256=YXuObz68eem2AND1q-RBeZ-X9Zi_IlUgx0DRMiwM3Xw,18706
32
32
  slidge/core/mixins/avatar.py,sha256=ke3cwm6Iiz1AM0_SiARTUTU2nGYskPZlxtFrXKQT5fI,7812
@@ -38,39 +38,43 @@ slidge/core/mixins/message.py,sha256=jJz_peNQmDf0uVQcI7TWG7oUaSGjUL8eiulNicGgWhI
38
38
  slidge/core/mixins/message_maker.py,sha256=TcCutHi0sIwL6beJNkN7XyR0aDIbA0xZyxd2Gc9ulG4,6022
39
39
  slidge/core/mixins/presence.py,sha256=yywo6KAw8C7GaZSMrSMuioNfhW08MrnobHt8XbHd0q8,7891
40
40
  slidge/core/mixins/recipient.py,sha256=U-YppozUO8pA94jmD3-qmhkykTebPNaOVWc3JDPC9w8,1302
41
- slidge/core/pubsub.py,sha256=QUwh1o5n9SVn1QBuflgfP4GR5yx6AJqRByDD3AGVCTY,12298
41
+ slidge/core/pubsub.py,sha256=ofiZEw0tgckpdJJW102fj7MovUXBrCHYufaHaad4oFo,12390
42
42
  slidge/core/session.py,sha256=IUZur7huNj066uy-Nh-I-R0etYmPJccgXFIimcSQ148,27161
43
43
  slidge/db/__init__.py,sha256=EBDH1JSEhgqYcli2Bw11CRC749wJk8AOucgBzmhDSvU,105
44
+ slidge/db/alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  slidge/db/alembic/env.py,sha256=hsBlRNs0zF5diSHGRSa8Fi3qRVQDA2rJdR41AEIdvxc,1642
46
+ slidge/db/alembic/old_user_store.py,sha256=zFOv0JEWQQK0_TMRlU4Z0G5Mc9pxvEErLyOzXmRAe5Q,5209
45
47
  slidge/db/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
46
48
  slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py,sha256=mUL-0Io6ZPd_QbnKfwGYyjdMcM2uxQ0Wg72H23-2t_E,1033
47
49
  slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py,sha256=CLB-kOP9Rc0FJIKDLef912L5sYkjpTIPC8fhrIdrC7k,1084
48
50
  slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py,sha256=f8TFS28CXjGhvIn41UYMoHYeODfqhKfo4O7gk-JwA1E,1134
49
51
  slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py,sha256=CMVP2wFz6s7t57eWdSaGtck8BXzfVPJhHE5AoWi34tI,1359
52
+ slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256=wk958rfBuop9mw6wI_dDQUeTdiCCV6GJb-WcP4h1xVs,1340
50
53
  slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py,sha256=g37po0ydp8ZmzJrE5oFV7GscnploxjCtPDpw28SqVGk,1429
54
+ slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py,sha256=18tG8B03Kq8Qz_-mMd28Beed6jow8XNTtrz7gT5QY3g,1210
51
55
  slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py,sha256=olXaOEEsUSasqaaKdlP1cBODsMhmV1i90qbpDM2vTm4,4696
52
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py,sha256=qBEw4KSf8NLn9piRxCn7VQbNS4XJQAfHIKmc2Rj1H5U,2320
56
+ slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py,sha256=XOf6Oiot2wLsngvUwrM30LbuVNm549FWXnKMJijBjVo,2578
53
57
  slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py,sha256=2tiRxoC9PYOQn6XQrwK0JTEsb45Pzp2PsKoZSS4rcIA,7564
54
58
  slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py,sha256=N6HYpFBhMckAFLZFW8PY8Us1qzXlauEQiDwEYwd9_K8,1422
55
59
  slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py,sha256=jjQmlRv6nqdm5q6LbwVpSUSkTBj1c76Hiq8e8q77q3g,933
56
60
  slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py,sha256=8Ga3VFgKrzMs_-B8OPtfP-0rey_MFaDg-QGtSbaft3o,640
57
61
  slidge/db/avatar.py,sha256=khMqkhUSLOOAulhWHUtkIlE8asdqUuaY8cieij97Hsw,7640
58
- slidge/db/meta.py,sha256=-MxJ66fI2hUs86geCTC4PSFDlRfJTvqJYvhdXydO6w8,1538
59
- slidge/db/models.py,sha256=r6AyhSJ18kOiEgiArZLhBxMD6a07xbZVOIQ0RsQ8Tmo,12834
60
- slidge/db/store.py,sha256=4QplqGaU8IPPf9MZU_P2LLZkbQB60GXDkxQrkkemfbc,40329
62
+ slidge/db/meta.py,sha256=v1Jf-npZ28QwdGpsLQWLBHEbEP3-jnPrygRg05tJ_Iw,1831
63
+ slidge/db/models.py,sha256=UoQnDYGuwp8VPYpqMKS9uFez_Vc8IMHtLADGlDCXC1Q,13179
64
+ slidge/db/store.py,sha256=rlDwapJhMM2iOpJsjn371qG14nuwZoS5Scd8txF3rWM,40817
61
65
  slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
62
66
  slidge/group/archive.py,sha256=xGPkdSk8-BT6t6lNVo1FEwiFVAttoxCma8Tsyk5r8Kg,5279
63
67
  slidge/group/bookmarks.py,sha256=NGEOTSrIIzSYVxUp2QlbW4cRNavPCaFLo21WrOjxBPc,6647
64
68
  slidge/group/participant.py,sha256=FhI8RjVMp5fpYMLxG38wWPI3GEke8rTs-tmLklCIpzA,16764
65
- slidge/group/room.py,sha256=Y5-0u7erskQF9L68htc4Yh6MnrzQwpT5zvIOrOah6fA,45032
66
- slidge/main.py,sha256=gIlF9MEtychpdeUcOE1PWe_QPlOmsQAYvpcOfwRNoQo,6119
69
+ slidge/group/room.py,sha256=OiV-qex_aGezGwOkKe2Uxscm7LA9F0NrvhRo-y-6Sjw,45232
70
+ slidge/main.py,sha256=TbkG5m1SgNmfaZ7TOD5VGTh4Q52XltLJayWnan8M9cw,5986
67
71
  slidge/migration.py,sha256=qUrux9dYCtOfFjPIlvGfWjoyqhkdxzaOByJRqTmCoUI,1072
68
72
  slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
73
  slidge/slixfix/__init__.py,sha256=7GevigEt68hwgwHqXcsFogN5jRXRHPeqR6kwODCH4hc,3055
70
74
  slidge/slixfix/link_preview/__init__.py,sha256=TDPTSEH5FQxgGpQpQIde-D72AHg-6YVWG-tOj4KpKmU,290
71
75
  slidge/slixfix/link_preview/link_preview.py,sha256=9PgdfnoyVMHnXS0w5OFp0wz3ku96Ck-HtRXbVUlDi1U,448
72
76
  slidge/slixfix/link_preview/stanza.py,sha256=YAXoNw2MD0a3nzvldGKlvSemjUMbUEG23regzmj4Ntc,2664
73
- slidge/slixfix/roster.py,sha256=zdcO8vsk8gqkqye-vqIynfj-pmozbjD-tRtoqwpKW7I,1680
77
+ slidge/slixfix/roster.py,sha256=KvDjh9q7pqaZf69H93okfib13cc95uVZUJ6rzpqmDaU,1704
74
78
  slidge/slixfix/xep_0077/__init__.py,sha256=0lY1YXdgAsfrfxI_Woxaf1etHCJXe35Xtntq_icF6nA,325
75
79
  slidge/slixfix/xep_0077/register.py,sha256=6nwTfHNL7Z9-1wUhpAF743TNbjQLCMP7Rflkdad8d60,10431
76
80
  slidge/slixfix/xep_0077/stanza.py,sha256=Lngly7F1ChCkNKn7yl1QmN838fO-KqkAhkazxzDsz80,2410
@@ -84,7 +88,7 @@ slidge/slixfix/xep_0264/__init__.py,sha256=c6g_y-PAwQJZ4ZLWcwXc6Q5xRPeXTvvvJH4ZK
84
88
  slidge/slixfix/xep_0264/stanza.py,sha256=YvkI9rsGztkc9yOZBjf5PNKReW8aeGdF6MnrsfDckYs,864
85
89
  slidge/slixfix/xep_0264/thumbnail.py,sha256=6ukgPCWJTFUnew4USB6hNtEk_ZcpWcFAvHr2r0T5znw,456
86
90
  slidge/slixfix/xep_0292/__init__.py,sha256=_MvS9wGra6ig3P_dPAVlCPDJkiOFvUWGjaRsHj1woUg,98
87
- slidge/slixfix/xep_0292/vcard4.py,sha256=uuZp_6DR1_-QuRNV-IfYwu9eX-wBT1jxh5p-PO92Nm8,3211
91
+ slidge/slixfix/xep_0292/vcard4.py,sha256=jL-TOW3eG2QXLduSLNq03L8HoUNmvy8kTZI5ojvo6GE,358
88
92
  slidge/slixfix/xep_0313/__init__.py,sha256=rpvXxN4Fi-ey4Ww39OEAXoiaeWs3XMqvzR64hA6j_x4,368
89
93
  slidge/slixfix/xep_0313/mam.py,sha256=2USgMGgklnGXPcw_1F3482HxIyd41TLx0uit43_RthA,9211
90
94
  slidge/slixfix/xep_0313/stanza.py,sha256=WriAx6XKiiyZTkoTw5RHcgu3ZYdkDd6hjQ0wHqDkXE0,10249
@@ -103,12 +107,11 @@ slidge/slixfix/xep_0490/stanza.py,sha256=ztec_ipyhUFz_uWQYkS0Q6LlsNiSBBC5aZK-qSm
103
107
  slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
104
108
  slidge/util/archive_msg.py,sha256=xXAR0BI5r3d6KKWjae9594izCOv6iI03z2WLuTecNw8,1724
105
109
  slidge/util/conf.py,sha256=1j2OnOsCBar1tOObErhXR5RC3Vl3faliOZ1U8J3My58,6613
106
- slidge/util/db.py,sha256=zFOv0JEWQQK0_TMRlU4Z0G5Mc9pxvEErLyOzXmRAe5Q,5209
107
- slidge/util/test.py,sha256=yRl3_dmNrn_v-FtOsRo_9eZr3Owj5JB5YIS0PozFU0E,13266
110
+ slidge/util/test.py,sha256=nCldxi9dLPj-c4LqZChR9DbzXFU4WgT0o_Pe80JJIDc,13279
108
111
  slidge/util/types.py,sha256=Gif-Z9NVd_eTei1uM3_KOsdok3B1yMQwdOkOzf9vheE,5224
109
112
  slidge/util/util.py,sha256=8JeE0QObNGQr_Tw4OFPwQSz_EB_zh_0t9IJmNNhW0ic,9114
110
- slidge-0.2.0a1.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
111
- slidge-0.2.0a1.dist-info/METADATA,sha256=_6VENlq93oEe42OuGW4DM-Riq0-08DziC-hTrb0fl5s,4723
112
- slidge-0.2.0a1.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
113
- slidge-0.2.0a1.dist-info/entry_points.txt,sha256=SNl72KSocF5plsu_67xyH6wVWfGTXQbzkQgXbLtzDrQ,47
114
- slidge-0.2.0a1.dist-info/RECORD,,
113
+ slidge-0.2.0a3.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
114
+ slidge-0.2.0a3.dist-info/METADATA,sha256=FHnY-NweSRtVFdxm75xTdbZD2MieNfqIvlagawopsWM,4723
115
+ slidge-0.2.0a3.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
116
+ slidge-0.2.0a3.dist-info/entry_points.txt,sha256=SNl72KSocF5plsu_67xyH6wVWfGTXQbzkQgXbLtzDrQ,47
117
+ slidge-0.2.0a3.dist-info/RECORD,,
File without changes