Mercurial > libervia-backend
comparison libervia/cli/cmd_bookmarks.py @ 4327:554a87ae17a6
plugin XEP-0048, XEP-0402; CLI (bookmarks): implement XEP-0402 (PEP Native Bookmarks):
- Former bookmarks implementation is now labeled as "legacy".
- XEP-0402 is now used for bookmarks when relevant namespaces are found, and it fallbacks
to legacy XEP-0048/XEP-0049 bookmarks otherwise.
- CLI legacy bookmark commands have been moved to `bookmarks legacy`
- CLI bookmarks commands now use the new XEP-0402 (with fallback to legacy one
automatically used if necessary).
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 20 Nov 2024 11:43:27 +0100 |
parents | 47401850dec6 |
children |
comparison
equal
deleted
inserted
replaced
4326:5fd6a4dc2122 | 4327:554a87ae17a6 |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from rich.table import Table | |
21 | |
22 from libervia.backend.core.i18n import _ | |
23 from libervia.backend.tools.common import data_format | |
24 from libervia.cli.constants import Const as C | |
25 | |
20 from . import base | 26 from . import base |
21 from libervia.backend.core.i18n import _ | 27 from .bookmarks_legacy import BookmarksLegacy |
22 from libervia.cli.constants import Const as C | |
23 | 28 |
24 __commands__ = ["Bookmarks"] | 29 __commands__ = ["Bookmarks"] |
25 | 30 |
26 STORAGE_LOCATIONS = ("local", "private", "pubsub") | |
27 TYPES = ("muc", "url") | |
28 | 31 |
29 | 32 class BookmarksList(base.CommandBase): |
30 class BookmarksCommon(base.CommandBase): | 33 def __init__(self, host): |
31 """Class used to group common options of bookmarks subcommands""" | 34 extra_outputs = {"default": self.default_output} |
32 | 35 super().__init__( |
33 def add_parser_options(self, location_default="all"): | 36 host, "list", help=_("list bookmarks"), |
34 self.parser.add_argument( | 37 use_output=C.OUTPUT_COMPLEX, |
35 "-l", | 38 extra_outputs=extra_outputs |
36 "--location", | |
37 type=str, | |
38 choices=(location_default,) + STORAGE_LOCATIONS, | |
39 default=location_default, | |
40 help=_("storage location (default: %(default)s)"), | |
41 ) | |
42 self.parser.add_argument( | |
43 "-t", | |
44 "--type", | |
45 type=str, | |
46 choices=TYPES, | |
47 default=TYPES[0], | |
48 help=_("bookmarks type (default: %(default)s)"), | |
49 ) | 39 ) |
50 | 40 |
41 def add_parser_options(self): | |
42 pass | |
51 | 43 |
52 class BookmarksList(BookmarksCommon): | 44 def default_output(self, data: dict) -> None: |
53 def __init__(self, host): | 45 table = Table(title="📚 " + _("Group Chat Bookmarks")) |
54 super(BookmarksList, self).__init__(host, "list", help=_("list bookmarks")) | 46 table.add_column("🌐 JID") |
47 table.add_column("📝 " + _("Name")) | |
48 table.add_column("👤 " + _("Nick")) | |
49 table.add_column("🔒 " + _("Password")) | |
50 table.add_column("🚪 " + _("Joined")) | |
51 | |
52 for jid, conference_data in data.items(): | |
53 table.add_row( | |
54 str(jid), | |
55 conference_data.get("name", ""), | |
56 conference_data.get("nick", ""), | |
57 conference_data.get("password", ""), | |
58 "✅" if conference_data.get("autojoin", False) else "❌" | |
59 ) | |
60 | |
61 self.console.print(table) | |
55 | 62 |
56 async def start(self): | 63 async def start(self): |
57 try: | 64 try: |
58 data = await self.host.bridge.bookmarks_list( | 65 data = data_format.deserialise(await self.host.bridge.bookmarks_list( |
59 self.args.type, self.args.location, self.host.profile | 66 "", self.host.profile |
60 ) | 67 )) |
61 except Exception as e: | 68 except Exception as e: |
62 self.disp(f"can't get bookmarks list: {e}", error=True) | 69 self.disp(f"can't get bookmarks list: {e}", error=True) |
63 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 70 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
71 return | |
64 | 72 |
65 mess = [] | 73 await self.output(data) |
66 for location in STORAGE_LOCATIONS: | |
67 if not data[location]: | |
68 continue | |
69 loc_mess = [] | |
70 loc_mess.append(f"{location}:") | |
71 book_mess = [] | |
72 for book_link, book_data in list(data[location].items()): | |
73 name = book_data.get("name") | |
74 autojoin = book_data.get("autojoin", "false") == "true" | |
75 nick = book_data.get("nick") | |
76 book_mess.append( | |
77 "\t%s[%s%s]%s" | |
78 % ( | |
79 (name + " ") if name else "", | |
80 book_link, | |
81 " (%s)" % nick if nick else "", | |
82 " (*)" if autojoin else "", | |
83 ) | |
84 ) | |
85 loc_mess.append("\n".join(book_mess)) | |
86 mess.append("\n".join(loc_mess)) | |
87 | |
88 print("\n\n".join(mess)) | |
89 self.host.quit() | 74 self.host.quit() |
90 | 75 |
91 | 76 |
92 class BookmarksRemove(BookmarksCommon): | 77 class BookmarksRemove(base.CommandBase): |
93 def __init__(self, host): | 78 def __init__(self, host): |
94 super(BookmarksRemove, self).__init__(host, "remove", help=_("remove a bookmark")) | 79 super().__init__(host, "remove", help=_("remove a bookmark")) |
95 | 80 |
96 def add_parser_options(self): | 81 def add_parser_options(self): |
97 super(BookmarksRemove, self).add_parser_options() | |
98 self.parser.add_argument( | 82 self.parser.add_argument( |
99 "bookmark", help=_("jid (for muc bookmark) or url of to remove") | 83 "bookmark", help=_("jid of the bookmark to remove") |
100 ) | 84 ) |
101 self.parser.add_argument( | 85 self.parser.add_argument( |
102 "-f", | 86 "-f", |
103 "--force", | 87 "--force", |
104 action="store_true", | 88 action="store_true", |
105 help=_("delete bookmark without confirmation"), | 89 help=_("delete bookmark without confirmation"), |
106 ) | 90 ) |
107 | 91 |
108 async def start(self): | 92 async def start(self): |
109 if not self.args.force: | 93 if not self.args.force: |
110 await self.host.confirm_or_quit(_("Are you sure to delete this bookmark?")) | 94 await self.host.confirm_or_quit( |
95 _("Are you sure to delete the bookmark {bookmark_id!r}?") | |
96 .format(bookmark_id=self.args.bookmark) | |
97 ) | |
111 | 98 |
112 try: | 99 try: |
113 await self.host.bridge.bookmarks_remove( | 100 await self.host.bridge.bookmark_remove( |
114 self.args.type, self.args.bookmark, self.args.location, self.host.profile | 101 self.args.bookmark, self.host.profile |
115 ) | 102 ) |
116 except Exception as e: | 103 except Exception as e: |
117 self.disp(_("can't delete bookmark: {e}").format(e=e), error=True) | 104 self.disp(_("can't delete bookmark: {e}").format(e=e), error=True) |
118 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 105 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
119 else: | 106 else: |
120 self.disp(_("bookmark deleted")) | 107 self.disp(_("bookmark deleted")) |
121 self.host.quit() | 108 self.host.quit() |
122 | 109 |
123 | 110 |
124 class BookmarksAdd(BookmarksCommon): | 111 class BookmarksSet(base.CommandBase): |
125 def __init__(self, host): | 112 def __init__(self, host): |
126 super(BookmarksAdd, self).__init__(host, "add", help=_("add a bookmark")) | 113 super().__init__( |
114 host, "set", help=_("add or update a bookmark") | |
115 ) | |
127 | 116 |
128 def add_parser_options(self): | 117 def add_parser_options(self): |
129 super(BookmarksAdd, self).add_parser_options(location_default="auto") | 118 self.parser.add_argument("bookmark", help=_("jid of the chat room")) |
119 self.parser.add_argument("-n", "--name", help=_("bookmark name"), dest="name") | |
130 self.parser.add_argument( | 120 self.parser.add_argument( |
131 "bookmark", help=_("jid (for muc bookmark) or url of to remove") | 121 "-j", |
122 "--join", | |
123 nargs="?", | |
124 # Value use when option is missing. | |
125 default=None, | |
126 # Value use when option is used, but value is not specified. | |
127 const=True, | |
128 type=base.optional_bool_decoder, | |
129 # The bookmark attribute is called "autojoin" for historical reason, but it's | |
130 # now used a "join" flag, so we use ``join`` here for the option. | |
131 dest="autojoin", | |
132 metavar="BOOL", | |
133 help=_("join the conference room"), | |
132 ) | 134 ) |
133 self.parser.add_argument("-n", "--name", help=_("bookmark name")) | 135 self.parser.add_argument( |
134 muc_group = self.parser.add_argument_group(_("MUC specific options")) | 136 "-N", |
135 muc_group.add_argument("-N", "--nick", help=_("nickname")) | 137 "--nick", help=_("preferred roomnick for the chatroom") |
136 muc_group.add_argument( | 138 ) |
137 "-a", | 139 self.parser.add_argument( |
138 "--autojoin", | 140 "-P", |
141 "--password", help=_("password used to access the chatroom") | |
142 ) | |
143 self.parser.add_argument( | |
144 "-u", | |
145 "--update", | |
139 action="store_true", | 146 action="store_true", |
140 help=_("join room on profile connection"), | 147 help=_("update bookmark data instead of replacing") |
141 ) | 148 ) |
142 | 149 |
143 async def start(self): | 150 async def start(self): |
144 if self.args.type == "url" and (self.args.autojoin or self.args.nick is not None): | 151 conference_data = { |
145 self.parser.error(_("You can't use --autojoin or --nick with --type url")) | 152 "autojoin": self.args.autojoin, |
146 data = {} | 153 "name": self.args.name, |
147 if self.args.autojoin: | 154 "nick": self.args.nick, |
148 data["autojoin"] = "true" | 155 "password": self.args.password, |
149 if self.args.nick is not None: | 156 } |
150 data["nick"] = self.args.nick | 157 |
151 if self.args.name is not None: | 158 conference_data = {k: v for k, v in conference_data.items() if v is not None} |
152 data["name"] = self.args.name | 159 if self.args.update: |
160 try: | |
161 old_conference_data = data_format.deserialise( | |
162 await self.host.bridge.bookmark_get( | |
163 self.args.bookmark, self.host.profile | |
164 ) | |
165 ) | |
166 except Exception as e: | |
167 self.disp( | |
168 f"Can't find existing bookmark {self.args.bookmark!r}: {e}. We " | |
169 "create it.", | |
170 error=True | |
171 ) | |
172 else: | |
173 old_conference_data.update(conference_data) | |
174 conference_data = old_conference_data | |
175 | |
153 try: | 176 try: |
154 await self.host.bridge.bookmarks_add( | 177 await self.host.bridge.bookmarks_set( |
155 self.args.type, | 178 data_format.serialise({self.args.bookmark: conference_data}), |
156 self.args.bookmark, | |
157 data, | |
158 self.args.location, | |
159 self.host.profile, | 179 self.host.profile, |
160 ) | 180 ) |
161 except Exception as e: | 181 except Exception as e: |
162 self.disp(f"can't add bookmark: {e}", error=True) | 182 self.disp(f"can't add bookmark: {e}", error=True) |
163 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 183 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
164 else: | 184 else: |
165 self.disp(_("bookmark successfully added")) | 185 self.disp(_("bookmark successfully set")) |
166 self.host.quit() | 186 self.host.quit() |
167 | 187 |
168 | 188 |
169 class Bookmarks(base.CommandBase): | 189 class Bookmarks(base.CommandBase): |
170 subcommands = (BookmarksList, BookmarksRemove, BookmarksAdd) | 190 subcommands = (BookmarksList, BookmarksSet, BookmarksRemove, BookmarksLegacy) |
171 | 191 |
172 def __init__(self, host): | 192 def __init__(self, host): |
173 super(Bookmarks, self).__init__( | 193 super().__init__( |
174 host, "bookmarks", use_profile=False, help=_("manage bookmarks") | 194 host, "bookmarks", use_profile=False, help=_("manage bookmarks") |
175 ) | 195 ) |