hive-nectar 0.0.6__py3-none-any.whl → 0.0.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hive-nectar might be problematic. Click here for more details.
- {hive_nectar-0.0.6.dist-info → hive_nectar-0.0.9.dist-info}/METADATA +5 -3
- hive_nectar-0.0.9.dist-info/RECORD +91 -0
- nectar/__init__.py +1 -0
- nectar/account.py +44 -48
- nectar/amount.py +6 -11
- nectar/block.py +8 -9
- nectar/blockchain.py +4 -5
- nectar/blockchaininstance.py +4 -4
- nectar/blockchainobject.py +5 -6
- nectar/blurt.py +3 -4
- nectar/cli.py +14 -14
- nectar/comment.py +10 -11
- nectar/community.py +527 -181
- nectar/conveyor.py +3 -4
- nectar/exceptions.py +30 -24
- nectar/hive.py +3 -4
- nectar/hivesigner.py +2 -2
- nectar/imageuploader.py +2 -3
- nectar/nodelist.py +83 -7
- nectar/price.py +6 -13
- nectar/rc.py +1 -2
- nectar/steem.py +3 -4
- nectar/storage.py +3 -4
- nectar/transactionbuilder.py +12 -3
- nectar/version.py +1 -1
- nectar/vote.py +8 -9
- nectar/wallet.py +1 -1
- nectarapi/__init__.py +1 -0
- nectarapi/exceptions.py +20 -14
- nectarapi/version.py +1 -1
- nectarbase/__init__.py +1 -0
- nectarbase/ledgertransactions.py +2 -3
- nectarbase/memo.py +9 -10
- nectarbase/objects.py +4 -5
- nectarbase/operations.py +3 -7
- nectarbase/version.py +1 -1
- nectargraphenebase/__init__.py +1 -1
- nectargraphenebase/account.py +16 -37
- nectargraphenebase/base58.py +5 -8
- nectargraphenebase/bip32.py +5 -11
- nectargraphenebase/bip38.py +6 -7
- nectargraphenebase/ecdsasig.py +32 -37
- nectargraphenebase/objects.py +6 -7
- nectargraphenebase/operations.py +2 -0
- nectargraphenebase/signedtransactions.py +10 -9
- nectargraphenebase/types.py +9 -19
- nectargraphenebase/unsignedtransactions.py +21 -28
- nectargraphenebase/version.py +1 -1
- nectarstorage/__init__.py +21 -1
- nectarstorage/masterpassword.py +2 -3
- nectarstorage/sqlite.py +1 -1
- hive_nectar-0.0.6.dist-info/RECORD +0 -92
- nectargraphenebase/py23.py +0 -38
- {hive_nectar-0.0.6.dist-info → hive_nectar-0.0.9.dist-info}/WHEEL +0 -0
- {hive_nectar-0.0.6.dist-info → hive_nectar-0.0.9.dist-info}/entry_points.txt +0 -0
- {hive_nectar-0.0.6.dist-info → hive_nectar-0.0.9.dist-info}/licenses/LICENSE.txt +0 -0
nectar/community.py
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
from datetime import date, datetime, time
|
|
5
|
+
from typing import Union
|
|
5
6
|
|
|
6
7
|
from prettytable import PrettyTable
|
|
7
8
|
|
|
8
9
|
from nectar.instance import shared_blockchain_instance
|
|
9
|
-
from nectargraphenebase.py23 import integer_types, string_types
|
|
10
10
|
|
|
11
11
|
from .blockchainobject import BlockchainObject
|
|
12
12
|
from .exceptions import AccountDoesNotExistsException, OfflineHasNoRPCException
|
|
@@ -19,27 +19,23 @@ log = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class Community(BlockchainObject):
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
:param str account: Name of the account
|
|
25
|
-
:param Steem/Hive blockchain_instance: Hive or Steem
|
|
26
|
-
instance
|
|
27
|
-
:param bool lazy: Use lazy loading
|
|
28
|
-
:param bool full: Obtain all account data including orders, positions,
|
|
29
|
-
etc.
|
|
30
|
-
:param Hive hive_instance: Hive instance
|
|
31
|
-
:param Steem steem_instance: Steem instance
|
|
32
|
-
:returns: Account data
|
|
33
|
-
:rtype: dictionary
|
|
34
|
-
:raises nectar.exceptions.AccountDoesNotExistsException: if account
|
|
35
|
-
does not exist
|
|
36
|
-
|
|
37
|
-
Instances of this class are dictionaries that come with additional
|
|
38
|
-
methods (see below) that allow dealing with an community and its
|
|
39
|
-
corresponding functions.
|
|
40
|
-
|
|
41
|
-
.. code-block:: python
|
|
22
|
+
"""A class representing a Hive community with methods to interact with it.
|
|
42
23
|
|
|
24
|
+
This class provides an interface to access and manipulate community data on the Hive blockchain.
|
|
25
|
+
It extends BlockchainObject and provides additional community-specific functionality.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
community: Either a community name (str) or a dictionary containing community data
|
|
29
|
+
observer: Observer account for personalized results (default: "")
|
|
30
|
+
full: If True, fetch full community data (default: True)
|
|
31
|
+
lazy: If True, use lazy loading (default: False)
|
|
32
|
+
blockchain_instance: Hive or Steem instance for blockchain access
|
|
33
|
+
**kwargs: Additional arguments including 'hive_instance' or 'steem_instance'
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
type_id (int): Type identifier for blockchain objects (2 for communities)
|
|
37
|
+
|
|
38
|
+
Example:
|
|
43
39
|
>>> from nectar.community import Community
|
|
44
40
|
>>> from nectar import Hive
|
|
45
41
|
>>> from nectar.nodelist import NodeList
|
|
@@ -49,28 +45,32 @@ class Community(BlockchainObject):
|
|
|
49
45
|
>>> community = Community("hive-139531", blockchain_instance=stm)
|
|
50
46
|
>>> print(community)
|
|
51
47
|
<Community hive-139531>
|
|
52
|
-
>>> print(community.balances) # doctest: +SKIP
|
|
53
|
-
|
|
54
|
-
.. note:: This class comes with its own caching function to reduce the
|
|
55
|
-
load on the API server. Instances of this class can be
|
|
56
|
-
refreshed with ``Community.refresh()``. The cache can be
|
|
57
|
-
cleared with ``Community.clear_cache()``
|
|
58
48
|
|
|
49
|
+
Note:
|
|
50
|
+
This class includes caching to reduce API server load. Use refresh() to update
|
|
51
|
+
the data and clear_cache() to clear the cache.
|
|
59
52
|
"""
|
|
60
53
|
|
|
61
54
|
type_id = 2
|
|
62
55
|
|
|
63
56
|
def __init__(
|
|
64
|
-
self,
|
|
65
|
-
|
|
66
|
-
""
|
|
67
|
-
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
self,
|
|
58
|
+
community: Union[str, dict],
|
|
59
|
+
observer: str = "",
|
|
60
|
+
full: bool = True,
|
|
61
|
+
lazy: bool = False,
|
|
62
|
+
blockchain_instance=None,
|
|
63
|
+
**kwargs,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Initialize a Community instance.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
community: Either a community name (str) or a dictionary containing community data
|
|
69
|
+
observer: Observer account for personalized results (default: "")
|
|
70
|
+
full: If True, fetch full community data (default: True)
|
|
71
|
+
lazy: If True, use lazy loading (default: False)
|
|
72
|
+
blockchain_instance: Hive or Steem instance for blockchain access
|
|
73
|
+
**kwargs: Additional arguments including 'hive_instance' or 'steem_instance'
|
|
74
74
|
"""
|
|
75
75
|
self.full = full
|
|
76
76
|
self.lazy = lazy
|
|
@@ -87,8 +87,16 @@ class Community(BlockchainObject):
|
|
|
87
87
|
community, lazy=lazy, full=full, id_item="name", blockchain_instance=blockchain_instance
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
def refresh(self):
|
|
91
|
-
"""Refresh
|
|
90
|
+
def refresh(self) -> None:
|
|
91
|
+
"""Refresh the community's data from the blockchain.
|
|
92
|
+
|
|
93
|
+
This method updates the community's data by fetching the latest information
|
|
94
|
+
from the blockchain. It raises an exception if the community doesn't exist.
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
AccountDoesNotExistsException: If the community doesn't exist on the blockchain
|
|
98
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
99
|
+
"""
|
|
92
100
|
if not self.blockchain.is_connected():
|
|
93
101
|
return
|
|
94
102
|
self.blockchain.rpc.set_next_node_on_empty_reply(True)
|
|
@@ -110,208 +118,402 @@ class Community(BlockchainObject):
|
|
|
110
118
|
blockchain_instance=self.blockchain,
|
|
111
119
|
)
|
|
112
120
|
|
|
113
|
-
def _parse_json_data(self, community):
|
|
114
|
-
|
|
121
|
+
def _parse_json_data(self, community: dict) -> dict:
|
|
122
|
+
"""Parse and convert community JSON data into proper Python types.
|
|
123
|
+
|
|
124
|
+
This internal method converts string representations of numbers to integers
|
|
125
|
+
and parses date strings into datetime objects with timezone information.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
community: Dictionary containing raw community data from the API
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
dict: Processed community data with proper Python types
|
|
132
|
+
"""
|
|
133
|
+
# Convert string numbers to integers
|
|
134
|
+
int_fields = [
|
|
115
135
|
"sum_pending",
|
|
116
136
|
"subscribers",
|
|
117
137
|
"num_pending",
|
|
118
138
|
"num_authors",
|
|
119
139
|
]
|
|
120
|
-
for
|
|
121
|
-
if
|
|
122
|
-
community[
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
for field in int_fields:
|
|
141
|
+
if field in community and isinstance(community.get(field), str):
|
|
142
|
+
community[field] = int(community.get(field, 0))
|
|
143
|
+
|
|
144
|
+
# Parse date strings into datetime objects
|
|
145
|
+
date_fields = ["created_at"]
|
|
146
|
+
for field in date_fields:
|
|
147
|
+
if field in community and isinstance(community.get(field), str):
|
|
148
|
+
community[field] = addTzInfo(
|
|
149
|
+
datetime.strptime(
|
|
150
|
+
community.get(field, "1970-01-01 00:00:00"), "%Y-%m-%d %H:%M:%S"
|
|
151
|
+
)
|
|
128
152
|
)
|
|
153
|
+
|
|
129
154
|
return community
|
|
130
155
|
|
|
131
|
-
def json(self):
|
|
156
|
+
def json(self) -> dict:
|
|
157
|
+
"""Convert the community data to a JSON-serializable dictionary.
|
|
158
|
+
|
|
159
|
+
This method prepares the community data for JSON serialization by converting
|
|
160
|
+
non-JSON-serializable types (like datetime objects) to strings.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
dict: A dictionary containing the community data in a JSON-serializable format
|
|
164
|
+
"""
|
|
132
165
|
output = self.copy()
|
|
133
|
-
|
|
166
|
+
|
|
167
|
+
# Convert integer fields to strings for JSON serialization
|
|
168
|
+
int_fields = [
|
|
134
169
|
"sum_pending",
|
|
135
170
|
"subscribers",
|
|
136
171
|
"num_pending",
|
|
137
172
|
"num_authors",
|
|
138
173
|
]
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
|
|
175
|
+
# Fields that should only be converted if non-zero
|
|
176
|
+
int_non_zero_fields = []
|
|
177
|
+
|
|
178
|
+
# Convert regular integer fields
|
|
179
|
+
for field in int_fields:
|
|
180
|
+
if field in output and isinstance(output[field], int):
|
|
181
|
+
output[field] = str(output[field])
|
|
182
|
+
|
|
183
|
+
# Convert non-zero integer fields
|
|
184
|
+
for field in int_non_zero_fields:
|
|
185
|
+
if field in output and isinstance(output[field], int) and output[field] != 0:
|
|
186
|
+
output[field] = str(output[field])
|
|
187
|
+
|
|
188
|
+
# Convert datetime fields to ISO format strings
|
|
189
|
+
date_fields = ["created_at"]
|
|
190
|
+
for field in date_fields:
|
|
191
|
+
if field in output:
|
|
192
|
+
date_val = output.get(field, datetime(1970, 1, 1, 0, 0))
|
|
193
|
+
if isinstance(date_val, (datetime, date, time)):
|
|
194
|
+
output[field] = formatTimeString(date_val).replace("T", " ")
|
|
155
195
|
else:
|
|
156
|
-
output[
|
|
196
|
+
output[field] = date_val
|
|
157
197
|
return json.loads(str(json.dumps(output)))
|
|
158
198
|
|
|
159
|
-
def get_community_roles(self):
|
|
160
|
-
"""Lists community roles
|
|
199
|
+
def get_community_roles(self, limit: int = 100, last: str = None) -> list:
|
|
200
|
+
"""Lists community roles
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
limit: Maximum number of roles to return (default: 100)
|
|
204
|
+
last: Account name of the last role from previous page for pagination
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
list: List of community roles
|
|
208
|
+
|
|
209
|
+
Raises:
|
|
210
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
211
|
+
"""
|
|
161
212
|
community = self["name"]
|
|
162
213
|
if not self.blockchain.is_connected():
|
|
163
214
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
215
|
+
|
|
216
|
+
params = {"community": community, "limit": limit}
|
|
217
|
+
if last is not None:
|
|
218
|
+
params["last"] = last
|
|
219
|
+
|
|
164
220
|
self.blockchain.rpc.set_next_node_on_empty_reply(False)
|
|
165
|
-
return self.blockchain.rpc.list_community_roles(
|
|
221
|
+
return self.blockchain.rpc.list_community_roles(params, api="bridge")
|
|
222
|
+
|
|
223
|
+
def get_subscribers(self, limit: int = 100, last: str = None) -> list:
|
|
224
|
+
"""Returns subscribers
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
limit: Maximum number of subscribers to return (default: 100)
|
|
228
|
+
last: Account name of the last subscriber from previous page for pagination
|
|
166
229
|
|
|
167
|
-
|
|
168
|
-
|
|
230
|
+
Returns:
|
|
231
|
+
list: List of subscribers
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
235
|
+
"""
|
|
169
236
|
community = self["name"]
|
|
170
237
|
if not self.blockchain.is_connected():
|
|
171
238
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
239
|
+
|
|
240
|
+
params = {"community": community, "limit": limit}
|
|
241
|
+
if last is not None:
|
|
242
|
+
params["last"] = last
|
|
243
|
+
|
|
172
244
|
self.blockchain.rpc.set_next_node_on_empty_reply(False)
|
|
173
|
-
return self.blockchain.rpc.list_subscribers(
|
|
245
|
+
return self.blockchain.rpc.list_subscribers(params, api="bridge")
|
|
246
|
+
|
|
247
|
+
def get_activities(self, limit: int = 100, last_id: str = None) -> list:
|
|
248
|
+
"""Returns community activity
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
limit: Maximum number of activities to return (default: 100)
|
|
252
|
+
last_id: ID of the last activity from previous page for pagination
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
list: List of community activities
|
|
174
256
|
|
|
175
|
-
|
|
176
|
-
|
|
257
|
+
Raises:
|
|
258
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
259
|
+
"""
|
|
177
260
|
community = self["name"]
|
|
178
261
|
if not self.blockchain.is_connected():
|
|
179
262
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
263
|
+
|
|
264
|
+
params = {"account": community, "limit": limit}
|
|
265
|
+
if last_id is not None:
|
|
266
|
+
params["last_id"] = last_id
|
|
267
|
+
|
|
180
268
|
self.blockchain.rpc.set_next_node_on_empty_reply(False)
|
|
181
|
-
return self.blockchain.rpc.account_notifications(
|
|
182
|
-
{"account": community, "limit": limit, "last_id": last_id}, api="bridge"
|
|
183
|
-
)
|
|
269
|
+
return self.blockchain.rpc.account_notifications(params, api="bridge")
|
|
184
270
|
|
|
185
271
|
def get_ranked_posts(
|
|
186
|
-
self,
|
|
187
|
-
|
|
188
|
-
|
|
272
|
+
self,
|
|
273
|
+
observer: str = None,
|
|
274
|
+
limit: int = 100,
|
|
275
|
+
start_author: str = None,
|
|
276
|
+
start_permlink: str = None,
|
|
277
|
+
sort: str = "created",
|
|
278
|
+
) -> list:
|
|
279
|
+
"""Returns community posts
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
observer: Account name of the observer (optional)
|
|
283
|
+
limit: Maximum number of posts to return (default: 100)
|
|
284
|
+
start_author: Author of the post to start from for pagination (optional)
|
|
285
|
+
start_permlink: Permlink of the post to start from for pagination (optional)
|
|
286
|
+
sort: Sort order (default: "created")
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
list: List of community posts
|
|
290
|
+
|
|
291
|
+
Raises:
|
|
292
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
293
|
+
"""
|
|
189
294
|
community = self["name"]
|
|
190
295
|
if not self.blockchain.is_connected():
|
|
191
296
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
297
|
+
|
|
298
|
+
params = {"tag": community, "limit": limit, "sort": sort}
|
|
299
|
+
|
|
300
|
+
if observer is not None:
|
|
301
|
+
params["observer"] = observer
|
|
302
|
+
if start_author is not None:
|
|
303
|
+
params["start_author"] = start_author
|
|
304
|
+
if start_permlink is not None:
|
|
305
|
+
params["start_permlink"] = start_permlink
|
|
306
|
+
|
|
192
307
|
self.blockchain.rpc.set_next_node_on_empty_reply(False)
|
|
193
|
-
return self.blockchain.rpc.get_ranked_posts(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
"limit": limit,
|
|
198
|
-
"start_author": start_author,
|
|
199
|
-
"start_permlink": start_permlink,
|
|
200
|
-
"sort": sort,
|
|
201
|
-
},
|
|
202
|
-
api="bridge",
|
|
203
|
-
)
|
|
308
|
+
return self.blockchain.rpc.get_ranked_posts(params, api="bridge")
|
|
309
|
+
|
|
310
|
+
def set_role(self, account: str, role: str, mod_account: str) -> dict:
|
|
311
|
+
"""Set role for a given account in the community.
|
|
204
312
|
|
|
205
|
-
|
|
206
|
-
|
|
313
|
+
Args:
|
|
314
|
+
account: Account name to set the role for
|
|
315
|
+
role: Role to assign (member, mod, admin, owner, or guest)
|
|
316
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
207
317
|
|
|
208
|
-
:
|
|
209
|
-
|
|
210
|
-
:param str mod_account: Account who broadcast this, (mods or higher)
|
|
318
|
+
Returns:
|
|
319
|
+
dict: Transaction result
|
|
211
320
|
|
|
321
|
+
Raises:
|
|
322
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
323
|
+
ValueError: If role is not one of the allowed values
|
|
212
324
|
"""
|
|
325
|
+
valid_roles = {"member", "mod", "admin", "owner", "guest"}
|
|
326
|
+
if role.lower() not in valid_roles:
|
|
327
|
+
raise ValueError(f"Invalid role. Must be one of: {', '.join(valid_roles)}")
|
|
328
|
+
|
|
213
329
|
community = self["name"]
|
|
214
330
|
if not self.blockchain.is_connected():
|
|
215
331
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
332
|
+
|
|
216
333
|
json_body = [
|
|
217
334
|
"setRole",
|
|
218
335
|
{
|
|
219
336
|
"community": community,
|
|
220
337
|
"account": account,
|
|
221
|
-
"role": role,
|
|
338
|
+
"role": role.lower(),
|
|
222
339
|
},
|
|
223
340
|
]
|
|
224
341
|
return self.blockchain.custom_json(
|
|
225
342
|
"community", json_body, required_posting_auths=[mod_account]
|
|
226
343
|
)
|
|
227
344
|
|
|
228
|
-
def set_user_title(self, account, title, mod_account):
|
|
229
|
-
"""Set title for a given account
|
|
345
|
+
def set_user_title(self, account: str, title: str, mod_account: str) -> dict:
|
|
346
|
+
"""Set the title for a given account in the community.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
account: Account name to set the title for
|
|
350
|
+
title: Title to assign to the account
|
|
351
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
230
352
|
|
|
231
|
-
:
|
|
232
|
-
|
|
233
|
-
:param str mod_account: Account who broadcast this, (mods or higher)
|
|
353
|
+
Returns:
|
|
354
|
+
dict: Transaction result
|
|
234
355
|
|
|
356
|
+
Raises:
|
|
357
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
358
|
+
ValueError: If account or title is empty
|
|
235
359
|
"""
|
|
360
|
+
if not account or not isinstance(account, str):
|
|
361
|
+
raise ValueError("Account must be a non-empty string")
|
|
362
|
+
|
|
363
|
+
if not title or not isinstance(title, str):
|
|
364
|
+
raise ValueError("Title must be a non-empty string")
|
|
365
|
+
|
|
236
366
|
community = self["name"]
|
|
237
367
|
if not self.blockchain.is_connected():
|
|
238
368
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
369
|
+
|
|
239
370
|
json_body = [
|
|
240
371
|
"setUserTitle",
|
|
241
372
|
{
|
|
242
373
|
"community": community,
|
|
243
374
|
"account": account,
|
|
244
|
-
"title": title,
|
|
375
|
+
"title": title.strip(),
|
|
245
376
|
},
|
|
246
377
|
]
|
|
247
378
|
return self.blockchain.custom_json(
|
|
248
379
|
"community", json_body, required_posting_auths=[mod_account]
|
|
249
380
|
)
|
|
250
381
|
|
|
251
|
-
def mute_post(self, account, permlink, notes, mod_account):
|
|
252
|
-
"""Mutes a post
|
|
382
|
+
def mute_post(self, account: str, permlink: str, notes: str, mod_account: str) -> dict:
|
|
383
|
+
"""Mutes a post in the community.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
account: Author of the post to mute
|
|
387
|
+
permlink: Permlink of the post to mute
|
|
388
|
+
notes: Reason for muting the post
|
|
389
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
253
390
|
|
|
254
|
-
:
|
|
255
|
-
|
|
256
|
-
:param str notes: permlink
|
|
257
|
-
:param str mod_account: Account who broadcast this, (mods or higher)
|
|
391
|
+
Returns:
|
|
392
|
+
dict: Transaction result
|
|
258
393
|
|
|
394
|
+
Raises:
|
|
395
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
396
|
+
ValueError: If any required parameter is invalid
|
|
259
397
|
"""
|
|
398
|
+
if not account or not isinstance(account, str):
|
|
399
|
+
raise ValueError("Account must be a non-empty string")
|
|
400
|
+
if not permlink or not isinstance(permlink, str):
|
|
401
|
+
raise ValueError("Permlink must be a non-empty string")
|
|
402
|
+
if not isinstance(notes, str):
|
|
403
|
+
raise ValueError("Notes must be a string")
|
|
404
|
+
if not mod_account or not isinstance(mod_account, str):
|
|
405
|
+
raise ValueError("Moderator account must be a non-empty string")
|
|
406
|
+
|
|
260
407
|
community = self["name"]
|
|
261
408
|
if not self.blockchain.is_connected():
|
|
262
409
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
410
|
+
|
|
263
411
|
json_body = [
|
|
264
412
|
"mutePost",
|
|
265
|
-
{
|
|
413
|
+
{
|
|
414
|
+
"community": community,
|
|
415
|
+
"account": account,
|
|
416
|
+
"permlink": permlink,
|
|
417
|
+
"notes": notes.strip(),
|
|
418
|
+
},
|
|
266
419
|
]
|
|
267
420
|
return self.blockchain.custom_json(
|
|
268
421
|
"community", json_body, required_posting_auths=[mod_account]
|
|
269
422
|
)
|
|
270
423
|
|
|
271
|
-
def unmute_post(self, account, permlink, notes, mod_account):
|
|
272
|
-
"""Unmute a post
|
|
424
|
+
def unmute_post(self, account: str, permlink: str, notes: str, mod_account: str) -> dict:
|
|
425
|
+
"""Unmute a previously muted post in the community.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
account: Author of the post to unmute
|
|
429
|
+
permlink: Permlink of the post to unmute
|
|
430
|
+
notes: Reason for unmuting the post
|
|
431
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
273
432
|
|
|
274
|
-
:
|
|
275
|
-
|
|
276
|
-
:param str notes: notes
|
|
277
|
-
:param str mod_account: Account who broadcast this, (mods or higher)
|
|
433
|
+
Returns:
|
|
434
|
+
dict: Transaction result
|
|
278
435
|
|
|
436
|
+
Raises:
|
|
437
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
438
|
+
ValueError: If any required parameter is invalid
|
|
279
439
|
"""
|
|
440
|
+
if not account or not isinstance(account, str):
|
|
441
|
+
raise ValueError("Account must be a non-empty string")
|
|
442
|
+
if not permlink or not isinstance(permlink, str):
|
|
443
|
+
raise ValueError("Permlink must be a non-empty string")
|
|
444
|
+
if not isinstance(notes, str):
|
|
445
|
+
raise ValueError("Notes must be a string")
|
|
446
|
+
if not mod_account or not isinstance(mod_account, str):
|
|
447
|
+
raise ValueError("Moderator account must be a non-empty string")
|
|
448
|
+
|
|
280
449
|
community = self["name"]
|
|
281
450
|
if not self.blockchain.is_connected():
|
|
282
451
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
452
|
+
|
|
283
453
|
json_body = [
|
|
284
454
|
"unmutePost",
|
|
285
|
-
{
|
|
455
|
+
{
|
|
456
|
+
"community": community,
|
|
457
|
+
"account": account,
|
|
458
|
+
"permlink": permlink,
|
|
459
|
+
"notes": notes.strip(),
|
|
460
|
+
},
|
|
286
461
|
]
|
|
287
462
|
return self.blockchain.custom_json(
|
|
288
463
|
"community", json_body, required_posting_auths=[mod_account]
|
|
289
464
|
)
|
|
290
465
|
|
|
291
|
-
def update_props(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
:
|
|
295
|
-
:
|
|
296
|
-
:
|
|
297
|
-
:
|
|
298
|
-
:
|
|
299
|
-
|
|
300
|
-
|
|
466
|
+
def update_props(
|
|
467
|
+
self,
|
|
468
|
+
title: str,
|
|
469
|
+
about: str,
|
|
470
|
+
is_nsfw: bool,
|
|
471
|
+
description: str,
|
|
472
|
+
flag_text: str,
|
|
473
|
+
admin_account: str,
|
|
474
|
+
) -> dict:
|
|
475
|
+
"""Update community properties.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
title: New title for the community (must be non-empty)
|
|
479
|
+
about: Brief description of the community
|
|
480
|
+
is_nsfw: Whether the community contains NSFW content
|
|
481
|
+
description: Detailed description of the community
|
|
482
|
+
flag_text: Text shown when flagging content in this community
|
|
483
|
+
admin_account: Account name of the admin performing this action
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
dict: Transaction result
|
|
487
|
+
|
|
488
|
+
Raises:
|
|
489
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
490
|
+
ValueError: If any required parameter is invalid
|
|
301
491
|
"""
|
|
492
|
+
if not title or not isinstance(title, str):
|
|
493
|
+
raise ValueError("Title must be a non-empty string")
|
|
494
|
+
if not isinstance(about, str):
|
|
495
|
+
about = ""
|
|
496
|
+
if not isinstance(description, str):
|
|
497
|
+
description = ""
|
|
498
|
+
if not isinstance(flag_text, str):
|
|
499
|
+
flag_text = ""
|
|
500
|
+
if not admin_account or not isinstance(admin_account, str):
|
|
501
|
+
raise ValueError("Admin account must be a non-empty string")
|
|
502
|
+
|
|
302
503
|
community = self["name"]
|
|
303
504
|
if not self.blockchain.is_connected():
|
|
304
505
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
506
|
+
|
|
305
507
|
json_body = [
|
|
306
508
|
"updateProps",
|
|
307
509
|
{
|
|
308
510
|
"community": community,
|
|
309
511
|
"props": {
|
|
310
|
-
"title": title,
|
|
311
|
-
"about": about,
|
|
312
|
-
"is_nsfw": is_nsfw,
|
|
313
|
-
"description": description,
|
|
314
|
-
"flag_text": flag_text,
|
|
512
|
+
"title": title.strip(),
|
|
513
|
+
"about": about.strip(),
|
|
514
|
+
"is_nsfw": bool(is_nsfw),
|
|
515
|
+
"description": description.strip(),
|
|
516
|
+
"flag_text": flag_text.strip(),
|
|
315
517
|
},
|
|
316
518
|
},
|
|
317
519
|
]
|
|
@@ -319,15 +521,29 @@ class Community(BlockchainObject):
|
|
|
319
521
|
"community", json_body, required_posting_auths=[admin_account]
|
|
320
522
|
)
|
|
321
523
|
|
|
322
|
-
def subscribe(self, account):
|
|
323
|
-
"""
|
|
524
|
+
def subscribe(self, account: str) -> dict:
|
|
525
|
+
"""Subscribe an account to this community.
|
|
526
|
+
|
|
527
|
+
The account that calls this method will be subscribed to the community.
|
|
528
|
+
The same account must be used to sign the transaction.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
account: Account name that wants to subscribe to the community
|
|
324
532
|
|
|
325
|
-
:
|
|
533
|
+
Returns:
|
|
534
|
+
dict: Transaction result
|
|
326
535
|
|
|
536
|
+
Raises:
|
|
537
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
538
|
+
ValueError: If account is invalid
|
|
327
539
|
"""
|
|
540
|
+
if not account or not isinstance(account, str):
|
|
541
|
+
raise ValueError("Account must be a non-empty string")
|
|
542
|
+
|
|
328
543
|
community = self["name"]
|
|
329
544
|
if not self.blockchain.is_connected():
|
|
330
545
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
546
|
+
|
|
331
547
|
json_body = [
|
|
332
548
|
"subscribe",
|
|
333
549
|
{
|
|
@@ -336,17 +552,35 @@ class Community(BlockchainObject):
|
|
|
336
552
|
]
|
|
337
553
|
return self.blockchain.custom_json("community", json_body, required_posting_auths=[account])
|
|
338
554
|
|
|
339
|
-
def pin_post(self, account, permlink, mod_account):
|
|
340
|
-
"""
|
|
555
|
+
def pin_post(self, account: str, permlink: str, mod_account: str) -> dict:
|
|
556
|
+
"""Pin a post to the top of the community feed.
|
|
557
|
+
|
|
558
|
+
This method allows community moderators to pin a specific post to the top of the
|
|
559
|
+
community's feed. The post will remain pinned until it is manually unpinned.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
account: Author of the post to pin
|
|
563
|
+
permlink: Permlink of the post to pin
|
|
564
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
341
565
|
|
|
342
|
-
:
|
|
343
|
-
|
|
344
|
-
:param str mod_account: Account who broadcast this, (mods or higher)
|
|
566
|
+
Returns:
|
|
567
|
+
dict: Transaction result
|
|
345
568
|
|
|
569
|
+
Raises:
|
|
570
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
571
|
+
ValueError: If any required parameter is invalid
|
|
346
572
|
"""
|
|
573
|
+
if not account or not isinstance(account, str):
|
|
574
|
+
raise ValueError("Account must be a non-empty string")
|
|
575
|
+
if not permlink or not isinstance(permlink, str):
|
|
576
|
+
raise ValueError("Permlink must be a non-empty string")
|
|
577
|
+
if not mod_account or not isinstance(mod_account, str):
|
|
578
|
+
raise ValueError("Moderator account must be a non-empty string")
|
|
579
|
+
|
|
347
580
|
community = self["name"]
|
|
348
581
|
if not self.blockchain.is_connected():
|
|
349
582
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
583
|
+
|
|
350
584
|
json_body = [
|
|
351
585
|
"pinPost",
|
|
352
586
|
{
|
|
@@ -359,15 +593,29 @@ class Community(BlockchainObject):
|
|
|
359
593
|
"community", json_body, required_posting_auths=[mod_account]
|
|
360
594
|
)
|
|
361
595
|
|
|
362
|
-
def unsubscribe(self, account):
|
|
363
|
-
"""
|
|
596
|
+
def unsubscribe(self, account: str) -> dict:
|
|
597
|
+
"""Unsubscribe an account from this community.
|
|
598
|
+
|
|
599
|
+
The account that calls this method will be unsubscribed from the community.
|
|
600
|
+
The same account must be used to sign the transaction.
|
|
364
601
|
|
|
365
|
-
:
|
|
602
|
+
Args:
|
|
603
|
+
account: Account name that wants to unsubscribe from the community
|
|
366
604
|
|
|
605
|
+
Returns:
|
|
606
|
+
dict: Transaction result
|
|
607
|
+
|
|
608
|
+
Raises:
|
|
609
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
610
|
+
ValueError: If account is invalid
|
|
367
611
|
"""
|
|
612
|
+
if not account or not isinstance(account, str):
|
|
613
|
+
raise ValueError("Account must be a non-empty string")
|
|
614
|
+
|
|
368
615
|
community = self["name"]
|
|
369
616
|
if not self.blockchain.is_connected():
|
|
370
617
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
618
|
+
|
|
371
619
|
json_body = [
|
|
372
620
|
"unsubscribe",
|
|
373
621
|
{
|
|
@@ -376,17 +624,35 @@ class Community(BlockchainObject):
|
|
|
376
624
|
]
|
|
377
625
|
return self.blockchain.custom_json("community", json_body, required_posting_auths=[account])
|
|
378
626
|
|
|
379
|
-
def unpin_post(self, account, permlink, mod_account):
|
|
380
|
-
"""
|
|
627
|
+
def unpin_post(self, account: str, permlink: str, mod_account: str) -> dict:
|
|
628
|
+
"""Remove a post from being pinned at the top of the community feed.
|
|
629
|
+
|
|
630
|
+
This method allows community moderators to unpin a previously pinned post.
|
|
631
|
+
After unpinning, the post will return to its normal position in the feed.
|
|
381
632
|
|
|
382
|
-
:
|
|
383
|
-
|
|
384
|
-
|
|
633
|
+
Args:
|
|
634
|
+
account: Author of the post to unpin
|
|
635
|
+
permlink: Permlink of the post to unpin
|
|
636
|
+
mod_account: Account name of the moderator performing this action (must be mod or higher)
|
|
385
637
|
|
|
638
|
+
Returns:
|
|
639
|
+
dict: Transaction result
|
|
640
|
+
|
|
641
|
+
Raises:
|
|
642
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
643
|
+
ValueError: If any required parameter is invalid
|
|
386
644
|
"""
|
|
645
|
+
if not account or not isinstance(account, str):
|
|
646
|
+
raise ValueError("Account must be a non-empty string")
|
|
647
|
+
if not permlink or not isinstance(permlink, str):
|
|
648
|
+
raise ValueError("Permlink must be a non-empty string")
|
|
649
|
+
if not mod_account or not isinstance(mod_account, str):
|
|
650
|
+
raise ValueError("Moderator account must be a non-empty string")
|
|
651
|
+
|
|
387
652
|
community = self["name"]
|
|
388
653
|
if not self.blockchain.is_connected():
|
|
389
654
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
655
|
+
|
|
390
656
|
json_body = [
|
|
391
657
|
"unpinPost",
|
|
392
658
|
{
|
|
@@ -399,20 +665,47 @@ class Community(BlockchainObject):
|
|
|
399
665
|
"community", json_body, required_posting_auths=[mod_account]
|
|
400
666
|
)
|
|
401
667
|
|
|
402
|
-
def flag_post(self, account, permlink, notes, reporter):
|
|
403
|
-
"""
|
|
668
|
+
def flag_post(self, account: str, permlink: str, notes: str, reporter: str) -> dict:
|
|
669
|
+
"""Report a post to the community moderators for review.
|
|
404
670
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
671
|
+
This method allows community members to flag posts that may violate
|
|
672
|
+
community guidelines. The post will be added to the community's
|
|
673
|
+
review queue for moderators to evaluate.
|
|
674
|
+
|
|
675
|
+
Args:
|
|
676
|
+
account: Author of the post being reported
|
|
677
|
+
permlink: Permlink of the post being reported
|
|
678
|
+
notes: Explanation of why the post is being reported
|
|
679
|
+
reporter: Account name of the user reporting the post
|
|
680
|
+
|
|
681
|
+
Returns:
|
|
682
|
+
dict: Transaction result
|
|
683
|
+
|
|
684
|
+
Raises:
|
|
685
|
+
OfflineHasNoRPCException: If not connected to the blockchain
|
|
686
|
+
ValueError: If any required parameter is invalid
|
|
409
687
|
"""
|
|
688
|
+
if not account or not isinstance(account, str):
|
|
689
|
+
raise ValueError("Account must be a non-empty string")
|
|
690
|
+
if not permlink or not isinstance(permlink, str):
|
|
691
|
+
raise ValueError("Permlink must be a non-empty string")
|
|
692
|
+
if not notes or not isinstance(notes, str):
|
|
693
|
+
raise ValueError("Notes must be a string")
|
|
694
|
+
if not reporter or not isinstance(reporter, str):
|
|
695
|
+
raise ValueError("Reporter account must be a non-empty string")
|
|
696
|
+
|
|
410
697
|
community = self["name"]
|
|
411
698
|
if not self.blockchain.is_connected():
|
|
412
699
|
raise OfflineHasNoRPCException("No RPC available in offline mode!")
|
|
700
|
+
|
|
413
701
|
json_body = [
|
|
414
702
|
"flagPost",
|
|
415
|
-
{
|
|
703
|
+
{
|
|
704
|
+
"community": community,
|
|
705
|
+
"account": account,
|
|
706
|
+
"permlink": permlink,
|
|
707
|
+
"notes": notes.strip(),
|
|
708
|
+
},
|
|
416
709
|
]
|
|
417
710
|
return self.blockchain.custom_json(
|
|
418
711
|
"community", json_body, required_posting_auths=[reporter]
|
|
@@ -420,7 +713,21 @@ class Community(BlockchainObject):
|
|
|
420
713
|
|
|
421
714
|
|
|
422
715
|
class CommunityObject(list):
|
|
423
|
-
|
|
716
|
+
"""A list-like container for Community objects with additional utility methods."""
|
|
717
|
+
|
|
718
|
+
def printAsTable(self) -> None:
|
|
719
|
+
"""Print a formatted table of communities with key metrics.
|
|
720
|
+
|
|
721
|
+
The table includes the following columns:
|
|
722
|
+
- Nr.: Sequential number
|
|
723
|
+
- Name: Community name
|
|
724
|
+
- Title: Community title
|
|
725
|
+
- lang: Language code
|
|
726
|
+
- subscribers: Number of subscribers
|
|
727
|
+
- sum_pending: Sum of pending payouts
|
|
728
|
+
- num_pending: Number of pending posts
|
|
729
|
+
- num_authors: Number of unique authors
|
|
730
|
+
"""
|
|
424
731
|
t = PrettyTable(
|
|
425
732
|
[
|
|
426
733
|
"Nr.",
|
|
@@ -453,26 +760,45 @@ class CommunityObject(list):
|
|
|
453
760
|
|
|
454
761
|
|
|
455
762
|
class Communities(CommunityObject):
|
|
456
|
-
"""
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
:
|
|
462
|
-
|
|
763
|
+
"""A list of communities with additional querying capabilities.
|
|
764
|
+
|
|
765
|
+
This class extends CommunityObject to provide methods for fetching and
|
|
766
|
+
searching communities from the blockchain.
|
|
767
|
+
|
|
768
|
+
Args:
|
|
769
|
+
sort: Sort order for communities (default: "rank")
|
|
770
|
+
observer: Observer account for personalized results (optional)
|
|
771
|
+
last: Last community name for pagination (optional)
|
|
772
|
+
limit: Maximum number of communities to fetch (default: 100)
|
|
773
|
+
lazy: If True, use lazy loading (default: False)
|
|
774
|
+
full: If True, fetch full community data (default: True)
|
|
775
|
+
blockchain_instance: Hive or Steem instance to use for blockchain access
|
|
776
|
+
**kwargs: Additional arguments including 'steem_instance' or 'hive_instance'
|
|
463
777
|
"""
|
|
464
778
|
|
|
465
779
|
def __init__(
|
|
466
780
|
self,
|
|
467
|
-
sort="rank",
|
|
468
|
-
observer=None,
|
|
469
|
-
last=None,
|
|
470
|
-
limit=100,
|
|
471
|
-
lazy=False,
|
|
472
|
-
full=True,
|
|
781
|
+
sort: str = "rank",
|
|
782
|
+
observer: str = None,
|
|
783
|
+
last: str = None,
|
|
784
|
+
limit: int = 100,
|
|
785
|
+
lazy: bool = False,
|
|
786
|
+
full: bool = True,
|
|
473
787
|
blockchain_instance=None,
|
|
474
788
|
**kwargs,
|
|
475
|
-
):
|
|
789
|
+
) -> None:
|
|
790
|
+
"""Initialize the Communities list with the given parameters.
|
|
791
|
+
|
|
792
|
+
Args:
|
|
793
|
+
sort: Sort order for communities (default: "rank")
|
|
794
|
+
observer: Observer account for personalized results (optional)
|
|
795
|
+
last: Last community name for pagination (optional)
|
|
796
|
+
limit: Maximum number of communities to fetch (default: 100)
|
|
797
|
+
lazy: If True, use lazy loading (default: False)
|
|
798
|
+
full: If True, fetch full community data (default: True)
|
|
799
|
+
blockchain_instance: Hive or Steem instance to use for blockchain access
|
|
800
|
+
**kwargs: Additional arguments including 'steem_instance' or 'hive_instance'
|
|
801
|
+
"""
|
|
476
802
|
if blockchain_instance is None:
|
|
477
803
|
if kwargs.get("steem_instance"):
|
|
478
804
|
blockchain_instance = kwargs["steem_instance"]
|
|
@@ -482,32 +808,52 @@ class Communities(CommunityObject):
|
|
|
482
808
|
|
|
483
809
|
if not self.blockchain.is_connected():
|
|
484
810
|
return
|
|
811
|
+
|
|
485
812
|
communities = []
|
|
486
813
|
community_cnt = 0
|
|
487
|
-
batch_limit = 100
|
|
488
|
-
if batch_limit > limit:
|
|
489
|
-
batch_limit = limit
|
|
814
|
+
batch_limit = min(100, limit) # Ensure we don't exceed the limit
|
|
490
815
|
|
|
491
816
|
while community_cnt < limit:
|
|
492
817
|
self.blockchain.rpc.set_next_node_on_empty_reply(False)
|
|
493
|
-
|
|
818
|
+
batch = self.blockchain.rpc.list_communities(
|
|
494
819
|
{"sort": sort, "observer": observer, "last": last, "limit": batch_limit},
|
|
495
820
|
api="bridge",
|
|
496
821
|
)
|
|
497
|
-
|
|
822
|
+
if not batch: # No more communities to fetch
|
|
823
|
+
break
|
|
824
|
+
|
|
825
|
+
communities.extend(batch)
|
|
826
|
+
community_cnt += len(batch)
|
|
498
827
|
last = communities[-1]["name"]
|
|
499
828
|
|
|
829
|
+
# Adjust batch size for the next iteration if needed
|
|
830
|
+
if community_cnt + batch_limit > limit:
|
|
831
|
+
batch_limit = limit - community_cnt
|
|
832
|
+
|
|
500
833
|
super(Communities, self).__init__(
|
|
501
834
|
[
|
|
502
835
|
Community(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
|
|
503
|
-
for x in communities
|
|
836
|
+
for x in communities[:limit] # Ensure we don't exceed the limit
|
|
504
837
|
]
|
|
505
838
|
)
|
|
506
839
|
|
|
507
|
-
def search_title(self, title):
|
|
508
|
-
"""
|
|
840
|
+
def search_title(self, title: str) -> "CommunityObject":
|
|
841
|
+
"""Search for communities with titles containing the given string.
|
|
842
|
+
|
|
843
|
+
The search is case-insensitive.
|
|
844
|
+
|
|
845
|
+
Args:
|
|
846
|
+
title: Text to search for in community titles
|
|
847
|
+
|
|
848
|
+
Returns:
|
|
849
|
+
CommunityObject: A new CommunityObject containing matching communities
|
|
850
|
+
"""
|
|
851
|
+
if not title or not isinstance(title, str):
|
|
852
|
+
raise ValueError("Title must be a non-empty string")
|
|
853
|
+
|
|
509
854
|
ret = CommunityObject()
|
|
855
|
+
title_lower = title.lower()
|
|
510
856
|
for community in self:
|
|
511
|
-
if
|
|
857
|
+
if title_lower in community["title"].lower():
|
|
512
858
|
ret.append(community)
|
|
513
859
|
return ret
|