slidge 0.1.3__py3-none-any.whl → 0.2.0a0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -196
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +8 -1
  5. slidge/command/admin.py +5 -6
  6. slidge/command/base.py +1 -2
  7. slidge/command/register.py +32 -16
  8. slidge/command/user.py +85 -5
  9. slidge/contact/contact.py +93 -31
  10. slidge/contact/roster.py +54 -39
  11. slidge/core/config.py +13 -7
  12. slidge/core/gateway/base.py +139 -34
  13. slidge/core/gateway/disco.py +2 -4
  14. slidge/core/gateway/mam.py +1 -4
  15. slidge/core/gateway/ping.py +2 -3
  16. slidge/core/gateway/presence.py +1 -1
  17. slidge/core/gateway/registration.py +32 -21
  18. slidge/core/gateway/search.py +3 -5
  19. slidge/core/gateway/session_dispatcher.py +100 -51
  20. slidge/core/gateway/vcard_temp.py +6 -4
  21. slidge/core/mixins/__init__.py +11 -1
  22. slidge/core/mixins/attachment.py +15 -10
  23. slidge/core/mixins/avatar.py +66 -18
  24. slidge/core/mixins/base.py +8 -2
  25. slidge/core/mixins/message.py +11 -7
  26. slidge/core/mixins/message_maker.py +17 -9
  27. slidge/core/mixins/presence.py +14 -4
  28. slidge/core/pubsub.py +54 -212
  29. slidge/core/session.py +65 -33
  30. slidge/db/__init__.py +4 -0
  31. slidge/db/alembic/env.py +64 -0
  32. slidge/db/alembic/script.py.mako +26 -0
  33. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  34. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  35. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
  36. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
  37. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  38. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  39. slidge/db/avatar.py +224 -0
  40. slidge/db/meta.py +65 -0
  41. slidge/db/models.py +365 -0
  42. slidge/db/store.py +976 -0
  43. slidge/group/archive.py +13 -14
  44. slidge/group/bookmarks.py +59 -56
  45. slidge/group/participant.py +77 -25
  46. slidge/group/room.py +242 -142
  47. slidge/main.py +201 -0
  48. slidge/migration.py +30 -0
  49. slidge/slixfix/__init__.py +35 -2
  50. slidge/slixfix/roster.py +11 -4
  51. slidge/slixfix/xep_0292/vcard4.py +1 -0
  52. slidge/util/db.py +1 -47
  53. slidge/util/test.py +21 -4
  54. slidge/util/types.py +24 -4
  55. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
  56. slidge-0.2.0a0.dist-info/RECORD +108 -0
  57. slidge/core/cache.py +0 -183
  58. slidge/util/schema.sql +0 -126
  59. slidge/util/sql.py +0 -508
  60. slidge-0.1.3.dist-info/RECORD +0 -96
  61. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
  62. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
  63. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/core/session.py CHANGED
@@ -18,11 +18,10 @@ from slixmpp.types import PresenceShows
18
18
 
19
19
  from ..command import SearchResult
20
20
  from ..contact import LegacyContact, LegacyRoster
21
+ from ..db.models import GatewayUser
21
22
  from ..group.bookmarks import LegacyBookmarks
22
23
  from ..group.room import LegacyMUC
23
24
  from ..util import ABCSubclassableOnceAtMost
24
- from ..util.db import GatewayUser, user_store
25
- from ..util.sql import SQLBiDict
26
25
  from ..util.types import (
27
26
  LegacyGroupIdType,
28
27
  LegacyMessageType,
@@ -92,16 +91,10 @@ class BaseSession(
92
91
  """
93
92
 
94
93
  def __init__(self, user: GatewayUser):
95
- self.log = logging.getLogger(user.bare_jid)
94
+ self.log = logging.getLogger(user.jid.bare)
96
95
 
97
- self.user = user
98
- self.sent = SQLBiDict[LegacyMessageType, str](
99
- "session_message_sent", "legacy_id", "xmpp_id", self.user
100
- )
101
- # message ids (*not* stanza-ids), needed for last msg correction
102
- self.muc_sent_msg_ids = SQLBiDict[LegacyMessageType, str](
103
- "session_message_sent_muc", "legacy_id", "xmpp_id", self.user
104
- )
96
+ self.user_jid = user.jid
97
+ self.user_pk = user.id
105
98
 
106
99
  self.ignore_messages = set[str]()
107
100
 
@@ -115,26 +108,26 @@ class BaseSession(
115
108
 
116
109
  self.http = self.xmpp.http
117
110
 
118
- self.threads = SQLBiDict[str, LegacyThreadType]( # type:ignore
119
- "session_thread_sent_muc", "legacy_id", "xmpp_id", self.user
120
- )
121
111
  self.thread_creation_lock = asyncio.Lock()
122
112
 
123
113
  self.__cached_presence: Optional[CachedPresence] = None
124
114
 
125
- self.avatar_hash: Optional[str] = None
126
-
127
115
  self.__tasks = set[asyncio.Task]()
128
116
 
117
+ @property
118
+ def user(self) -> GatewayUser:
119
+ return self.xmpp.store.users.get(self.user_jid) # type:ignore
120
+
129
121
  def __remove_task(self, fut):
130
122
  self.log.debug("Removing fut %s", fut)
131
123
  self.__tasks.remove(fut)
132
124
 
133
- def create_task(self, coro) -> None:
125
+ def create_task(self, coro) -> asyncio.Task:
134
126
  task = self.xmpp.loop.create_task(coro)
135
127
  self.__tasks.add(task)
136
128
  self.log.debug("Creating task %s", task)
137
129
  task.add_done_callback(lambda _: self.__remove_task(task))
130
+ return task
138
131
 
139
132
  def cancel_all_tasks(self):
140
133
  for task in self.__tasks:
@@ -488,6 +481,17 @@ class BaseSession(
488
481
  """
489
482
  await muc.on_set_affiliation(contact, "member", reason, None)
490
483
 
484
+ async def on_leave_group(self, muc: LegacyMUC):
485
+ """
486
+ Triggered when the user leaves a group via the dedicated slidge command
487
+ or the :xep:`0077` ``<remove />`` mechanism.
488
+
489
+ This should be interpreted as definitely leaving the group.
490
+
491
+ :param muc: The group to leave
492
+ """
493
+ raise NotImplementedError
494
+
491
495
  def __reset_ready(self):
492
496
  self.ready = self.xmpp.loop.create_future()
493
497
 
@@ -507,7 +511,7 @@ class BaseSession(
507
511
  self.ready.set_result(True)
508
512
 
509
513
  def __repr__(self):
510
- return f"<Session of {self.user}>"
514
+ return f"<Session of {self.user_jid}>"
511
515
 
512
516
  def shutdown(self) -> asyncio.Task:
513
517
  for c in self.contacts:
@@ -571,9 +575,9 @@ class BaseSession(
571
575
  log.debug("user not found", stack_info=True)
572
576
  raise XMPPError(text="User not found", condition="subscription-required")
573
577
 
574
- session = _sessions.get(user)
578
+ session = _sessions.get(user.jid.bare)
575
579
  if session is None:
576
- _sessions[user] = session = cls(user)
580
+ _sessions[user.jid.bare] = session = cls(user)
577
581
  return session
578
582
 
579
583
  @classmethod
@@ -590,7 +594,7 @@ class BaseSession(
590
594
  # :param s:
591
595
  # :return:
592
596
  # """
593
- return cls._from_user_or_none(user_store.get_by_stanza(s))
597
+ return cls.from_jid(s.get_from())
594
598
 
595
599
  @classmethod
596
600
  def from_jid(cls, jid: JID) -> "BaseSession":
@@ -602,7 +606,11 @@ class BaseSession(
602
606
  # :param jid:
603
607
  # :return:
604
608
  # """
605
- return cls._from_user_or_none(user_store.get_by_jid(jid))
609
+ session = _sessions.get(jid.bare)
610
+ if session is not None:
611
+ return session
612
+ user = cls.xmpp.store.users.get(jid)
613
+ return cls._from_user_or_none(user)
606
614
 
607
615
  @classmethod
608
616
  async def kill_by_jid(cls, jid: JID):
@@ -615,16 +623,21 @@ class BaseSession(
615
623
  # :return:
616
624
  # """
617
625
  log.debug("Killing session of %s", jid)
618
- for user, session in _sessions.items():
619
- if user.jid == jid.bare:
626
+ for user_jid, session in _sessions.items():
627
+ if user_jid == jid.bare:
620
628
  break
621
629
  else:
622
630
  log.debug("Did not find a session for %s", jid)
623
631
  return
624
632
  for c in session.contacts:
625
633
  c.unsubscribe()
634
+ user = cls.xmpp.store.users.get(jid)
635
+ if user is None:
636
+ log.warning("User not found during unregistration")
637
+ return
626
638
  await cls.xmpp.unregister(user)
627
- del _sessions[user]
639
+ cls.xmpp.store.users.delete(user.jid)
640
+ del _sessions[user.jid.bare]
628
641
  del user
629
642
  del session
630
643
 
@@ -649,7 +662,7 @@ class BaseSession(
649
662
  """
650
663
  self.__cached_presence = CachedPresence(status, show, kwargs)
651
664
  self.xmpp.send_presence(
652
- pto=self.user.bare_jid, pstatus=status, pshow=show, **kwargs
665
+ pto=self.user_jid.bare, pstatus=status, pshow=show, **kwargs
653
666
  )
654
667
 
655
668
  def send_cached_presence(self, to: JID):
@@ -671,7 +684,7 @@ class BaseSession(
671
684
 
672
685
  :param text: A text
673
686
  """
674
- self.xmpp.send_text(text, mto=self.user.jid, **msg_kwargs)
687
+ self.xmpp.send_text(text, mto=self.user_jid, **msg_kwargs)
675
688
 
676
689
  def send_gateway_invite(
677
690
  self,
@@ -686,7 +699,7 @@ class BaseSession(
686
699
  :param reason:
687
700
  :param password:
688
701
  """
689
- self.xmpp.invite_to(muc, reason=reason, password=password, mto=self.user.jid)
702
+ self.xmpp.invite_to(muc, reason=reason, password=password, mto=self.user_jid)
690
703
 
691
704
  async def input(self, text: str, **msg_kwargs):
692
705
  """
@@ -698,7 +711,7 @@ class BaseSession(
698
711
  :param msg_kwargs: Extra attributes
699
712
  :return:
700
713
  """
701
- return await self.xmpp.input(self.user.jid, text, **msg_kwargs)
714
+ return await self.xmpp.input(self.user_jid, text, **msg_kwargs)
702
715
 
703
716
  async def send_qr(self, text: str):
704
717
  """
@@ -707,7 +720,7 @@ class BaseSession(
707
720
 
708
721
  :param text: Text to encode as a QR code
709
722
  """
710
- await self.xmpp.send_qr(text, mto=self.user.jid)
723
+ await self.xmpp.send_qr(text, mto=self.user_jid)
711
724
 
712
725
  def re_login(self):
713
726
  # Logout then re-login
@@ -718,8 +731,8 @@ class BaseSession(
718
731
  async def get_contact_or_group_or_participant(self, jid: JID):
719
732
  if jid.bare in (contacts := self.contacts.known_contacts(only_friends=False)):
720
733
  return contacts[jid.bare]
721
- if jid.bare in (mucs := self.bookmarks._mucs_by_bare_jid):
722
- return await self.__get_muc_or_participant(mucs[jid.bare], jid)
734
+ if (muc := self.bookmarks.by_jid_only_if_exists(JID(jid.bare))) is not None:
735
+ return await self.__get_muc_or_participant(muc, jid)
723
736
  else:
724
737
  muc = None
725
738
 
@@ -763,6 +776,25 @@ class BaseSession(
763
776
  "Legacy session is not fully initialized, retry later",
764
777
  )
765
778
 
779
+ def legacy_module_data_update(self, data: dict):
780
+ with self.xmpp.store.session():
781
+ user = self.user
782
+ user.legacy_module_data.update(data)
783
+ self.xmpp.store.users.update(user)
784
+
785
+ def legacy_module_data_set(self, data: dict):
786
+ with self.xmpp.store.session():
787
+ user = self.user
788
+ user.legacy_module_data = data
789
+ self.xmpp.store.users.update(user)
790
+
791
+ def legacy_module_data_clear(self):
792
+ with self.xmpp.store.session():
793
+ user = self.user
794
+ user.legacy_module_data.clear()
795
+ self.xmpp.store.users.update(user)
796
+
766
797
 
767
- _sessions: dict[GatewayUser, BaseSession] = {}
798
+ # keys = user.jid.bare
799
+ _sessions: dict[str, BaseSession] = {}
768
800
  log = logging.getLogger(__name__)
slidge/db/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .models import GatewayUser
2
+ from .store import SlidgeStore
3
+
4
+ __all__ = ("GatewayUser", "SlidgeStore")
@@ -0,0 +1,64 @@
1
+ from alembic import context
2
+
3
+ from slidge import global_config
4
+ from slidge.db.meta import Base, get_engine
5
+
6
+ config = context.config
7
+
8
+ target_metadata = Base.metadata
9
+
10
+
11
+ def run_migrations_offline() -> None:
12
+ """Run migrations in 'offline' mode.
13
+
14
+ This configures the context with just a URL
15
+ and not an Engine, though an Engine is acceptable
16
+ here as well. By skipping the Engine creation
17
+ we don't even need a DBAPI to be available.
18
+
19
+ Calls to context.execute() here emit the given string to the
20
+ script output.
21
+
22
+ """
23
+ url = config.get_main_option("sqlalchemy.url")
24
+ context.configure(
25
+ url=url,
26
+ target_metadata=target_metadata,
27
+ literal_binds=True,
28
+ dialect_opts={"paramstyle": "named"},
29
+ render_as_batch=True,
30
+ )
31
+
32
+ with context.begin_transaction():
33
+ context.run_migrations()
34
+
35
+
36
+ def run_migrations_online() -> None:
37
+ """Run migrations in 'online' mode.
38
+
39
+ In this scenario we need to create an Engine
40
+ and associate a connection with the context.
41
+
42
+ """
43
+ try:
44
+ # in prod
45
+ connectable = get_engine(global_config.DB_URL)
46
+ except AttributeError:
47
+ # during dev, to generate migrations
48
+ connectable = get_engine("sqlite+pysqlite:///dev/slidge.sqlite")
49
+
50
+ with connectable.connect() as connection:
51
+ context.configure(
52
+ connection=connection,
53
+ target_metadata=target_metadata,
54
+ render_as_batch=True,
55
+ )
56
+
57
+ with context.begin_transaction():
58
+ context.run_migrations()
59
+
60
+
61
+ if context.is_offline_mode():
62
+ run_migrations_offline()
63
+ else:
64
+ run_migrations_online()
@@ -0,0 +1,26 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ ${imports if imports else ""}
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = ${repr(up_revision)}
16
+ down_revision: Union[str, None] = ${repr(down_revision)}
17
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19
+
20
+
21
+ def upgrade() -> None:
22
+ ${upgrades if upgrades else "pass"}
23
+
24
+
25
+ def downgrade() -> None:
26
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,36 @@
1
+ """Add n_participants attributes to Room
2
+
3
+ Should have been part of another commit, but I messed up some rebase
4
+
5
+ Revision ID: 09f27f098baa
6
+ Revises: 29f5280c61aa
7
+ Create Date: 2024-07-11 10:54:21.155871
8
+
9
+ """
10
+
11
+ from typing import Sequence, Union
12
+
13
+ import sqlalchemy as sa
14
+ from alembic import op
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "09f27f098baa"
18
+ down_revision: Union[str, None] = "29f5280c61aa"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ with op.batch_alter_table("room", schema=None) as batch_op:
26
+ batch_op.add_column(sa.Column("n_participants", sa.Integer(), nullable=True))
27
+
28
+ # ### end Alembic commands ###
29
+
30
+
31
+ def downgrade() -> None:
32
+ # ### commands auto generated by Alembic - please adjust! ###
33
+ with op.batch_alter_table("room", schema=None) as batch_op:
34
+ batch_op.drop_column("n_participants")
35
+
36
+ # ### end Alembic commands ###
@@ -0,0 +1,37 @@
1
+ """Store subject setter in Room
2
+
3
+ Revision ID: 29f5280c61aa
4
+ Revises: 8d2ced764698
5
+ Create Date: 2024-07-10 13:09:25.181594
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 = "29f5280c61aa"
16
+ down_revision: Union[str, None] = "8d2ced764698"
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
+ with op.batch_alter_table("room", schema=None) as batch_op:
23
+ batch_op.add_column(sa.Column("subject_setter_id", sa.Integer(), nullable=True))
24
+ # we give this constraint a name a workaround for
25
+ # https://github.com/sqlalchemy/alembic/issues/1195
26
+ batch_op.create_foreign_key(
27
+ "subject_setter_id_foreign_key",
28
+ "participant",
29
+ ["subject_setter_id"],
30
+ ["id"],
31
+ )
32
+
33
+
34
+ def downgrade() -> None:
35
+ with op.batch_alter_table("room", schema=None) as batch_op:
36
+ batch_op.drop_constraint("subject_setter_id_foreign_key", type_="foreignkey")
37
+ batch_op.drop_column("subject_setter_id")
@@ -0,0 +1,133 @@
1
+ """Rely on DB to store contacts, rooms and participants
2
+
3
+ Revision ID: 8d2ced764698
4
+ Revises: b33993e87db3
5
+ Create Date: 2024-07-08 14:39:47.022088
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ import slidge.db.meta
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "8d2ced764698"
18
+ down_revision: Union[str, None] = "b33993e87db3"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ op.create_table(
25
+ "hat",
26
+ sa.Column("id", sa.Integer(), nullable=False),
27
+ sa.Column("title", sa.String(), nullable=False),
28
+ sa.Column("uri", sa.String(), nullable=False),
29
+ sa.PrimaryKeyConstraint("id"),
30
+ sa.UniqueConstraint("title", "uri"),
31
+ )
32
+ op.create_table(
33
+ "contact_sent",
34
+ sa.Column("id", sa.Integer(), nullable=False),
35
+ sa.Column("contact_id", sa.Integer(), nullable=False),
36
+ sa.Column("msg_id", sa.String(), nullable=False),
37
+ sa.ForeignKeyConstraint(
38
+ ["contact_id"],
39
+ ["contact.id"],
40
+ ),
41
+ sa.PrimaryKeyConstraint("id"),
42
+ sa.UniqueConstraint("contact_id", "msg_id"),
43
+ )
44
+ op.create_table(
45
+ "participant",
46
+ sa.Column("id", sa.Integer(), nullable=False),
47
+ sa.Column("room_id", sa.Integer(), nullable=False),
48
+ sa.Column("contact_id", sa.Integer(), nullable=True),
49
+ sa.Column("is_user", sa.Boolean(), nullable=False),
50
+ sa.Column(
51
+ "affiliation",
52
+ sa.Enum("outcast", "member", "admin", "owner", "none", native_enum=False),
53
+ nullable=False,
54
+ ),
55
+ sa.Column(
56
+ "role",
57
+ sa.Enum("moderator", "participant", "visitor", "none", native_enum=False),
58
+ nullable=False,
59
+ ),
60
+ sa.Column("presence_sent", sa.Boolean(), nullable=False),
61
+ sa.Column("resource", sa.String(), nullable=True),
62
+ sa.Column("nickname", sa.String(), nullable=True),
63
+ sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
64
+ sa.ForeignKeyConstraint(
65
+ ["contact_id"],
66
+ ["contact.id"],
67
+ ),
68
+ sa.ForeignKeyConstraint(
69
+ ["room_id"],
70
+ ["room.id"],
71
+ ),
72
+ sa.PrimaryKeyConstraint("id"),
73
+ )
74
+ op.create_table(
75
+ "participant_hats",
76
+ sa.Column("participant_id", sa.Integer(), nullable=False),
77
+ sa.Column("hat_id", sa.Integer(), nullable=False),
78
+ sa.ForeignKeyConstraint(
79
+ ["hat_id"],
80
+ ["hat.id"],
81
+ ),
82
+ sa.ForeignKeyConstraint(
83
+ ["participant_id"],
84
+ ["participant.id"],
85
+ ),
86
+ sa.PrimaryKeyConstraint("participant_id", "hat_id"),
87
+ )
88
+ op.add_column("contact", sa.Column("is_friend", sa.Boolean(), nullable=False))
89
+ op.add_column("contact", sa.Column("added_to_roster", sa.Boolean(), nullable=False))
90
+ op.add_column(
91
+ "contact",
92
+ sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
93
+ )
94
+ op.add_column("contact", sa.Column("updated", sa.Boolean(), nullable=False))
95
+ op.add_column("room", sa.Column("description", sa.String(), nullable=True))
96
+ op.add_column("room", sa.Column("subject", sa.String(), nullable=True))
97
+ op.add_column("room", sa.Column("subject_date", sa.DateTime(), nullable=True))
98
+ op.add_column(
99
+ "room",
100
+ sa.Column(
101
+ "muc_type",
102
+ sa.Enum("GROUP", "CHANNEL", "CHANNEL_NON_ANONYMOUS", name="muctype"),
103
+ nullable=True,
104
+ ),
105
+ )
106
+ op.add_column("room", sa.Column("user_resources", sa.String(), nullable=True))
107
+ op.add_column(
108
+ "room", sa.Column("participants_filled", sa.Boolean(), nullable=False)
109
+ )
110
+ op.add_column(
111
+ "room",
112
+ sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
113
+ )
114
+ op.add_column("room", sa.Column("updated", sa.Boolean(), nullable=False))
115
+
116
+
117
+ def downgrade() -> None:
118
+ op.drop_column("room", "updated")
119
+ op.drop_column("room", "extra_attributes")
120
+ op.drop_column("room", "participants_filled")
121
+ op.drop_column("room", "user_resources")
122
+ op.drop_column("room", "muc_type")
123
+ op.drop_column("room", "subject_date")
124
+ op.drop_column("room", "subject")
125
+ op.drop_column("room", "description")
126
+ op.drop_column("contact", "updated")
127
+ op.drop_column("contact", "extra_attributes")
128
+ op.drop_column("contact", "added_to_roster")
129
+ op.drop_column("contact", "is_friend")
130
+ op.drop_table("participant_hats")
131
+ op.drop_table("participant")
132
+ op.drop_table("contact_sent")
133
+ op.drop_table("hat")
@@ -0,0 +1,76 @@
1
+ """DB Creation
2
+
3
+ Including a migration from the user_store shelf
4
+
5
+ Revision ID: aa9d82a7f6ef
6
+ Revises:
7
+ Create Date: 2024-04-17 20:57:01.357041
8
+
9
+ """
10
+
11
+ import logging
12
+ from typing import Sequence, Union
13
+
14
+ import sqlalchemy as sa
15
+ from alembic import op
16
+
17
+ import slidge.db.meta
18
+
19
+ # revision identifiers, used by Alembic.
20
+ revision: str = "aa9d82a7f6ef"
21
+ down_revision: Union[str, None] = None
22
+ branch_labels: Union[str, Sequence[str], None] = None
23
+ depends_on: Union[str, Sequence[str], None] = None
24
+
25
+
26
+ def upgrade() -> None:
27
+ # ### commands auto generated by Alembic - please adjust! ###
28
+ accounts = op.create_table(
29
+ "user_account",
30
+ sa.Column("id", sa.Integer(), nullable=False),
31
+ sa.Column("jid", slidge.db.meta.JIDType(), nullable=False),
32
+ sa.Column(
33
+ "registration_date",
34
+ sa.DateTime(),
35
+ server_default=sa.text("(CURRENT_TIMESTAMP)"),
36
+ nullable=False,
37
+ ),
38
+ sa.Column(
39
+ "legacy_module_data", slidge.db.meta.JSONEncodedDict(), nullable=False
40
+ ),
41
+ sa.Column("preferences", slidge.db.meta.JSONEncodedDict(), nullable=False),
42
+ sa.PrimaryKeyConstraint("id"),
43
+ sa.UniqueConstraint("jid"),
44
+ )
45
+ # ### end Alembic commands ###
46
+ migrate_from_shelf(accounts)
47
+
48
+
49
+ def downgrade() -> None:
50
+ # ### commands auto generated by Alembic - please adjust! ###
51
+ op.drop_table("user_account")
52
+ # ### end Alembic commands ###
53
+
54
+
55
+ def migrate_from_shelf(accounts: sa.Table) -> None:
56
+ try:
57
+ from slidge.util.db import user_store
58
+ except ImportError:
59
+ return
60
+ try:
61
+ users = list(user_store.get_all())
62
+ except AttributeError:
63
+ return
64
+ logging.info("Migrating %s users from the deprecated user_store shelf", len(users))
65
+ op.bulk_insert(
66
+ accounts,
67
+ [
68
+ {
69
+ "jid": user.jid,
70
+ "registration_date": user.registration_date,
71
+ "legacy_module_data": user.registration_form,
72
+ "preferences": {},
73
+ }
74
+ for user in users
75
+ ],
76
+ )