11#!/usr/bin/env python3
2+ import argparse
3+ import json
24import os
3- import sys
45from xml .dom import minidom # nosec
56
67
78def load_smileys (path : str ) -> dict [str , list [str ]]:
8- """Load smileys from emoticons.xml file.
9+ """Loads smileys from emoticons.xml file.
910
1011 Args:
1112 path: Path to the emoticons.xml file.
1213
13- Returns:
14- A dictionary where the keys are the filenames (without suffix) of the
14+ Returns
15+ -------
16+ smileys: A dictionary where the keys are the filenames (without suffix) of the
1517 smileys and the values are the strings that will be replaced by the
1618 file when sent in a message.
19+
1720 """
1821 smileys : dict [str , list [str ]] = {}
1922 dom = minidom .parse (path ) # nosec
@@ -33,12 +36,12 @@ def save_smileys(path: str, smileys: dict[str, list[str]]) -> None:
3336 Args:
3437 path: Path to the emoticons.xml file.
3538 smileys: The same format as the return value of load_smileys.
39+
3640 """
3741 doc = minidom .Document ()
3842 root = doc .createElement ("messaging-emoticon-map" )
3943 root .setAttribute ("xmlns:xsi" , "http://www.w3.org/2001/XMLSchema-instance" )
40- root .setAttribute ("xsi:noNamespaceSchemaLocation" ,
41- "../messaging-emoticon-map.xsd" )
44+ root .setAttribute ("xsi:noNamespaceSchemaLocation" , "../messaging-emoticon-map.xsd" )
4245 doc .appendChild (root )
4346 for file , strings in smileys .items ():
4447 emoticon = doc .createElement ("emoticon" )
@@ -69,11 +72,13 @@ def emoji_to_string(emoji: tuple[int, ...]) -> str:
6972
7073def add_missing_smileys (path : str , smileys : dict [str , list [str ]]) -> None :
7174 """Add smileys that exist in the path but not in the smileys dict."""
72- for emoji_str in sorted (filter_svgs (os .listdir (path )),
73- key = parse_emoji_sequence ):
75+ for emoji_str in sorted (filter_svgs (os .listdir (path )), key = parse_emoji_sequence ):
7476 emoji = emoji_to_string (parse_emoji_sequence (emoji_str ))
75- if (emoji_str not in smileys or len (smileys [emoji_str ]) == 1
76- and smileys [emoji_str ][0 ] == emoji ):
77+ if (
78+ emoji_str not in smileys
79+ or len (smileys [emoji_str ]) == 1
80+ and smileys [emoji_str ][0 ] == emoji
81+ ):
7782 smileys [emoji_str ] = [emoji ]
7883 if emoji not in smileys [emoji_str ]:
7984 smileys [emoji_str ].append (emoji )
@@ -95,7 +100,8 @@ def prefer_emoji(emoji: str, string: str) -> str:
95100
96101
97102def sort_strings (smileys : dict [str , list [str ]]) -> None :
98- """Sort the strings in the smileys dict.
103+ """
104+ Sort the strings in the smileys dict.
99105
100106 We put the emoji string first.
101107 """
@@ -104,15 +110,181 @@ def sort_strings(smileys: dict[str, list[str]]) -> None:
104110 strings .sort (key = lambda s : prefer_emoji (emoji , s ))
105111
106112
113+ def block_smiley_maybe (
114+ smileys : dict [str , list [str ]],
115+ blocked_smileys_in_pack : dict [str , list [str ]],
116+ block : str | None ,
117+ ) -> bool :
118+ """Block smiley defined in block and return True if blocked smiley list was modified.
119+
120+ Args
121+ ----
122+ smileys: The loaded set of smileys.
123+ blocked_smileys_in_pack: the list of currently blocked smileys.
124+ block: The smiley to be blocked.
125+
126+ Returns
127+ -------
128+ True if blocked_smileys_in_pack was modified, False otherwise.
129+
130+ """
131+ is_dirty = False
132+ if not block :
133+ return is_dirty
134+ block_name = None
135+ for name , strings in smileys .items ():
136+ smiley_set = set (strings )
137+ if block in smiley_set :
138+ block_name = name
139+ block_list = blocked_smileys_in_pack .get (block_name , [])
140+ if block not in block_list :
141+ # Add blocked smiley.
142+ block_list .append (block )
143+ blocked_smileys_in_pack [block_name ] = block_list
144+ is_dirty = True
145+ else :
146+ print (f'The smiley "{ block } " is already blocked.' )
147+ break
148+ if not block_name :
149+ is_blocked = False
150+ for name , strings in blocked_smileys_in_pack .items ():
151+ if block in strings :
152+ print (f'The smiley "{ block } " is already blocked.' )
153+ break
154+ if is_blocked :
155+ print (f'The smiley to block "{ block } " was not found.' )
156+ return is_dirty
157+
158+
159+ def unblock_smiley_maybe (
160+ smileys : dict [str , list [str ]],
161+ blocked_smileys_in_pack : dict [str , list [str ]],
162+ unblock : str | None ,
163+ ) -> bool :
164+ """Unblocks smiley defined in unblock and return True if blocked smiley list was modified.
165+
166+ Args:
167+ smileys: The loaded set of smileys.
168+ blocked_smileys_in_pack: the list of currently blocked smileys.
169+ unblock: The smiley to be unblocked.
170+
171+ Returns
172+ -------
173+ True if blocked_smileys_in_pack was modified, False otherwise.
174+
175+ """
176+ is_dirty = False
177+ unblock_name = None
178+ if not unblock :
179+ return is_dirty
180+ for name , strings in blocked_smileys_in_pack .items ():
181+ smiley_set = set (strings )
182+ if unblock in smiley_set :
183+ # Unblock smiley.
184+ unblock_name = name
185+ blocked_smileys_in_pack [unblock_name ].remove (unblock )
186+ smiley_list = smileys .get (unblock_name , [])
187+ smiley_list .append (unblock )
188+ smileys [unblock_name ] = list (set (smiley_list ))
189+ is_dirty = True
190+ break
191+ if not unblock_name :
192+ print (f'The smiley to unblock "{ unblock } " was not found in the blocklist.' )
193+ return is_dirty
194+
195+
196+ def load_and_update_blocklist (
197+ smileypack : str ,
198+ smileys : dict [str , list [str ]],
199+ block : str | None ,
200+ unblock : str | None ,
201+ ) -> dict [str , list [str ]]:
202+ """Loads the smiley block list, update and save it.
203+
204+ We also add unblocked smileys to the smileys dictionary.
205+
206+ Args:
207+ smileypack: The name of a smiley pack to update/load.
208+ smileys: The loaded set of smileys.
209+ block: The smiley to be blocked.
210+ unblock: The smiley to be unblocked.
211+
212+ Returns
213+ -------
214+ The dictionary, containing where the keys are the filenames (without suffix) of the
215+ smileys and the values are strings that will be replaced by the
216+ file when sent in a message.
217+
218+ """
219+ if block == unblock :
220+ raise ValueError ("The smiley cannot be blocked and unblocked simultaneously." )
221+ block_list_file = os .path .join (os .path .dirname (__file__ ), "blocked_smileys.json" )
222+ blocked_smileys : dict [str , dict [str , set [str ]]] = {}
223+ if os .path .isfile (block_list_file ):
224+ with open (block_list_file , "r" ) as f :
225+ blocked_smileys : dict [str , set [str ]] = json .load (f )
226+ blocked_smileys_in_pack = blocked_smileys .get (smileypack , {})
227+ # Update the dictionary of blocked smileys if needed.
228+ is_dirty = block_smiley_maybe (smileys , blocked_smileys_in_pack , block )
229+ # Search for the smiley to unblock in the block list and remove it.
230+ is_dirty = (
231+ unblock_smiley_maybe (smileys , blocked_smileys_in_pack , unblock ) or is_dirty
232+ )
233+
234+ if is_dirty :
235+ sort_strings (blocked_smileys_in_pack )
236+ blocked_smileys [smileypack ] = blocked_smileys_in_pack
237+ with open (block_list_file , "w" ) as f :
238+ json .dump (blocked_smileys , f , ensure_ascii = False , indent = 2 )
239+ return blocked_smileys .get (smileypack , {})
240+
241+
242+ def remove_blocked_smileys (
243+ smileys : dict [str , list [str ]], blocked_smileys : dict [str , list [str ]]
244+ ) -> None :
245+ """Removed all blocked_smileys from smileys.
246+
247+ Args:
248+ smileys: The loaded set of smileys.
249+ blocked_smileys: The loaded smileys to be blocked.
250+
251+ """
252+ for fname , strings in blocked_smileys .items ():
253+ if fname in smileys :
254+ smileys [fname ] = list (set (smileys [fname ]).difference (set (strings )))
255+
256+
107257def main () -> None :
108- if len (sys .argv ) != 2 :
109- print (f"Usage: { sys .argv [0 ]} <smileypack>" )
110- sys .exit (1 )
111- smileys = load_smileys (os .path .join (sys .argv [1 ], "emoticons.xml" ))
112- add_missing_smileys (sys .argv [1 ], smileys )
113- remove_missing_smileys (sys .argv [1 ], smileys )
258+ parser = argparse .ArgumentParser (
259+ description = "The script to parse and fix smileys directories."
260+ )
261+ parser .add_argument ("smileypack" , help = "The folder with smileys." )
262+ parser .add_argument (
263+ "-b" ,
264+ "--block-smiley" ,
265+ help = "The smiley to be added to blocklist." ,
266+ required = False ,
267+ default = None ,
268+ )
269+ parser .add_argument (
270+ "-u" ,
271+ "--unblock-smiley" ,
272+ help = "The smiley to be removed from blocklist." ,
273+ required = False ,
274+ default = None ,
275+ )
276+ args = parser .parse_args ()
277+ smileys = load_smileys (
278+ os .path .join (os .path .dirname (__file__ ), args .smileypack , "emoticons.xml" )
279+ )
280+ add_missing_smileys (args .smileypack , smileys )
281+ remove_missing_smileys (args .smileypack , smileys )
282+ blocked_smileys = load_and_update_blocklist (
283+ args .smileypack , smileys , args .block_smiley , args .unblock_smiley
284+ )
285+ remove_blocked_smileys (smileys , blocked_smileys )
114286 sort_strings (smileys )
115- save_smileys (os .path .join (sys . argv [ 1 ] , "emoticons.xml" ), smileys )
287+ save_smileys (os .path .join (args . smileypack , "emoticons.xml" ), smileys )
116288
117289
118290if __name__ == "__main__" :
0 commit comments