1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2009-2012 Sebastien Helleu <flashcode@flashtux.org>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # WeeChat scripts manager.
21 # (this script requires WeeChat >= 0.3.0 and python >= 2.6)
25 # 2012-03-09, Sebastien Helleu <flashcode@flashtux.org>:
26 # version 1.8: fix reload of config file
27 # 2012-02-27, Sebastien Helleu <flashcode@flashtux.org>:
28 # version 1.7: add support of scheme scripts
29 # 2012-02-05, Sebastien Helleu <flashcode@flashtux.org>:
30 # version 1.6: use URL transfer from API (for WeeChat >= 0.3.7)
31 # 2012-01-03, Sebastien Helleu <flashcode@flashtux.org>:
32 # version 1.5: make script compatible with Python 3.x
33 # 2011-03-25, Sebastien Helleu <flashcode@flashtux.org>:
34 # version 1.4: add completion with installed scripts for action "remove"
35 # 2011-03-10, Sebastien Helleu <flashcode@flashtux.org>:
36 # version 1.3: add script extension in script name completion and a new
37 # completion with tags for actions "list" and "listinstalled"
38 # 2011-02-13, Sebastien Helleu <flashcode@flashtux.org>:
39 # version 1.2: use new help format for command arguments
40 # 2010-11-08, Sebastien Helleu <flashcode@flashtux.org>:
41 # version 1.1: get python 2.x binary for hook_process (fix problem
42 # when python 3.x is default python version, requires
44 # 2010-02-22, Blake Winton <bwinton@latte.ca>:
45 # version 1.0: add action "listinstalled" for command /weeget
46 # 2010-01-25, Sebastien Helleu <flashcode@flashtux.org>:
47 # version 0.9: fix "running" status of scripts with /weeget check
48 # 2009-09-30, Sebastien Helleu <flashcode@flashtux.org>:
49 # version 0.8: fix bugs and add missing info in "/weeget show",
50 # display warning if url for plugins.xml.gz is old site
51 # 2009-09-07, Sebastien Helleu <flashcode@flashtux.org>:
52 # version 0.7: update weechat site with new URL
53 # 2009-05-02, Sebastien Helleu <flashcode@flashtux.org>:
54 # version 0.6: sync with last API changes
55 # 2009-04-15, Sebastien Helleu <flashcode@flashtux.org>:
56 # version 0.5: display missing module(s) when import failed
57 # 2009-04-11, Sebastien Helleu <flashcode@flashtux.org>:
58 # version 0.4: use new completion for command arguments
59 # 2009-04-07, Sebastien Helleu <flashcode@flashtux.org>:
60 # version 0.3: fix bug with install/upgrade when weeget is updated with
61 # other scripts: ensure that weeget is always the last
63 # 2009-04-07, Sebastien Helleu <flashcode@flashtux.org>:
64 # version 0.2: add author's mail in script description
65 # 2009-04-05, Sebastien Helleu <flashcode@flashtux.org>:
66 # version 0.1: initial release
69 SCRIPT_NAME
= "weeget"
70 SCRIPT_AUTHOR
= "Sebastien Helleu <flashcode@flashtux.org>"
71 SCRIPT_VERSION
= "1.8"
72 SCRIPT_LICENSE
= "GPL3"
73 SCRIPT_DESC
= "WeeChat scripts manager"
75 SCRIPT_COMMAND
= "weeget"
82 print("This script must be run under WeeChat.")
83 print("Get WeeChat now at: http://www.weechat.org/")
87 import sys
, os
, stat
, time
, gzip
, hashlib
, xml
.dom
.minidom
88 except ImportError as message
:
89 print("Missing package(s) for %s: %s" % (SCRIPT_NAME
, message
))
92 CONFIG_FILE_NAME
= "wg"
103 # timeout for download of plugins.xml.gz
104 TIMEOUT_UPDATE
= 60 * 1000
106 # timeout for download of a script
107 TIMEOUT_SCRIPT
= 60 * 1000
109 # config file and options
111 wg_config_option
= {}
113 # action (install, remove, ..) and arguments
118 wg_loaded_scripts
= {}
120 # hook process and stdout
121 wg_hook_process
= { "update": "", "script": "" }
122 wg_stdout
= { "update": "", "script": "" }
124 # scripts read from plugins.xml.gz
127 # list of script to install, and script currently installing
128 wg_scripts_to_install
= []
129 wg_current_script_install
= {}
131 # =================================[ config ]=================================
133 def wg_config_init():
135 Initialization of configuration file.
136 Sections: color, scripts.
138 global wg_config_file
, wg_config_option
139 wg_config_file
= weechat
.config_new(CONFIG_FILE_NAME
,
140 "wg_config_reload_cb", "")
141 if wg_config_file
== "":
145 section_color
= weechat
.config_new_section(
146 wg_config_file
, "color", 0, 0, "", "", "", "", "", "", "", "", "", "")
147 if section_color
== "":
148 weechat
.config_free(wg_config_file
)
150 wg_config_option
["color_script"] = weechat
.config_new_option(
151 wg_config_file
, section_color
,
152 "script", "color", "Color for script names", "", 0, 0,
153 "cyan", "cyan", 0, "", "", "", "", "", "")
154 wg_config_option
["color_installed"] = weechat
.config_new_option(
155 wg_config_file
, section_color
,
156 "installed", "color", "Color for \"installed\" indicator", "", 0, 0,
157 "yellow", "yellow", 0, "", "", "", "", "", "")
158 wg_config_option
["color_running"] = weechat
.config_new_option(
159 wg_config_file
, section_color
,
160 "running", "color", "Color for \"running\" indicator", "", 0, 0,
161 "lightgreen", "lightgreen", 0, "", "", "", "", "", "")
162 wg_config_option
["color_obsolete"] = weechat
.config_new_option(
163 wg_config_file
, section_color
,
164 "obsolete", "color", "Color for \"obsolete\" indicator", "", 0, 0,
165 "lightmagenta", "lightmagenta", 0, "", "", "", "", "", "")
166 wg_config_option
["color_unknown"] = weechat
.config_new_option(
167 wg_config_file
, section_color
,
168 "unknown", "color", "Color for \"unknown status\" indicator", "", 0, 0,
169 "lightred", "lightred", 0, "", "", "", "", "", "")
170 wg_config_option
["color_language"] = weechat
.config_new_option(
171 wg_config_file
, section_color
,
172 "language", "color", "Color for language names", "", 0, 0,
173 "lightblue", "lightblue", 0, "", "", "", "", "", "")
176 section_scripts
= weechat
.config_new_section(
177 wg_config_file
, "scripts", 0, 0, "", "", "", "", "", "", "", "", "", "")
178 if section_scripts
== "":
179 weechat
.config_free(wg_config_file
)
181 wg_config_option
["scripts_url"] = weechat
.config_new_option(
182 wg_config_file
, section_scripts
,
183 "url", "string", "URL for file with list of plugins", "", 0, 0,
184 "http://www.weechat.org/files/plugins.xml.gz",
185 "http://www.weechat.org/files/plugins.xml.gz", 0, "", "", "", "", "", "")
186 wg_config_option
["scripts_dir"] = weechat
.config_new_option(
187 wg_config_file
, section_scripts
,
188 "dir", "string", "Local cache directory for" + SCRIPT_NAME
, "", 0, 0,
189 "%h/" + SCRIPT_NAME
, "%h/" + SCRIPT_NAME
, 0, "", "", "", "", "", "")
190 wg_config_option
["scripts_cache_expire"] = weechat
.config_new_option(
191 wg_config_file
, section_scripts
,
192 "cache_expire", "integer", "Local cache expiration time, in minutes "
193 "(-1 = never expires, 0 = always expires)", "",
194 -1, 60*24*365, "60", "60", 0, "", "", "", "", "", "")
196 def wg_config_reload_cb(data
, config_file
):
197 """ Reload configuration file. """
198 return weechat
.config_reload(config_file
)
200 def wg_config_read():
201 """ Read configuration file. """
202 global wg_config_file
203 return weechat
.config_read(wg_config_file
)
205 def wg_config_write():
206 """ Write configuration file. """
207 global wg_config_file
208 return weechat
.config_write(wg_config_file
)
210 def wg_config_color(color
):
211 """ Get a color from configuration. """
212 global wg_config_option
213 option
= wg_config_option
.get("color_" + color
, "")
216 return weechat
.color(weechat
.config_string(option
))
218 def wg_config_get_dir():
219 """ Return weeget directory, with expanded WeeChat home dir. """
220 global wg_config_option
221 return weechat
.config_string(
222 wg_config_option
["scripts_dir"]).replace("%h",
223 weechat
.info_get("weechat_dir", ""))
225 def wg_config_create_dir():
226 """ Create weeget directory. """
227 dir = wg_config_get_dir()
228 if not os
.path
.isdir(dir):
229 os
.makedirs(dir, mode
=0o700)
231 def wg_config_get_cache_filename():
232 """ Get local cache filename, based on URL. """
233 global wg_config_option
234 return wg_config_get_dir() + os
.sep
+ \
235 os
.path
.basename(weechat
.config_string(wg_config_option
["scripts_url"]))
237 # =============================[ download file ]==============================
239 def wg_download_file(url
, filename
, timeout
, callback
, callback_data
):
240 """Download a file with an URL. Return hook_process created."""
241 version
= weechat
.info_get("version_number", "") or 0
242 if int(version
) >= 0x00030700:
243 return weechat
.hook_process_hashtable("url:%s" % url
,
244 { "file_out": filename }
,
246 callback
, callback_data
)
248 script
= [ "import sys",
250 " if sys.version_info >= (3,):",
251 " import urllib.request",
252 " response = urllib.request.urlopen('%s')" % url
,
255 " response = urllib2.urlopen(urllib2.Request('%s'))" % url
,
256 " f = open('%s', 'wb')" % filename
,
257 " f.write(response.read())",
260 "except Exception as e:",
261 " print('error:' + str(e))" ]
262 return weechat
.hook_process("python -c \"%s\"" % "\n".join(script
),
264 callback
, callback_data
)
266 # ================================[ scripts ]=================================
268 def wg_search_script_by_name(name
):
270 Search a script in list by name.
271 Name can be short name ('weeget') or full name ('weeget.py').
274 for id, script
in wg_scripts
.items():
275 if script
["name"] == name
or script
["full_name"] == name
:
279 def wg_get_loaded_scripts():
281 Get python dictionary with loaded scripts.
282 Keys are filenames and values are path to script, for example:
283 'weeget.py': '/home/xxx/.weechat/python/weeget.py'
285 global wg_loaded_scripts
286 wg_loaded_scripts
= {}
287 for language
in SCRIPT_EXTENSION
.keys():
288 infolist
= weechat
.infolist_get(language
+ "_script", "", "")
289 while weechat
.infolist_next(infolist
):
290 filename
= weechat
.infolist_string(infolist
, "filename")
292 wg_loaded_scripts
[os
.path
.basename(filename
)] = filename
293 weechat
.infolist_free(infolist
)
295 def wg_is_local_script_loaded(filename
):
296 """ Check if a script filename (like 'python/weeget.py') is loaded. """
297 global wg_loaded_scripts
299 if filename2
.startswith("autoload/"):
300 filename2
= filename2
[9:]
301 for name
, path
in wg_loaded_scripts
.items():
302 if path
.endswith(filename
) or path
.endswith(filename2
):
306 def wg_get_local_script_status(script
):
308 Check if a script is installed.
309 'script' is a dictionary retrieved from scripts xml list.
311 global wg_loaded_scripts
312 status
= { "installed": "", "obsolete": "", "running": "" }
313 local_dir
= weechat
.info_get("weechat_dir", "") + os
.sep
+ script
["language"]
314 local_name
= local_dir
+ os
.sep
+ "autoload" + os
.sep
+ script
["full_name"]
315 if not os
.path
.isfile(local_name
):
316 local_name
= local_dir
+ os
.sep
+ script
["full_name"]
317 if os
.path
.isfile(local_name
):
318 status
["installed"] = "1"
319 f
= open(local_name
, "rb")
323 local_md5
= md5
.hexdigest()
324 if local_md5
!= script
["md5sum"]:
325 status
["obsolete"] = "1"
326 if script
["full_name"] in wg_loaded_scripts
.keys():
327 status
["running"] = "1"
330 def wg_get_local_scripts():
332 Get list of all local scripts (in languages and autoload dirs).
333 Return a dictionary with language as key and list of paths as value,
334 with autoloaded scripts at beginning of list, for example:
335 { 'perl': [ 'autoload/buffers.pl',
336 'autoload/weetris.pl',
339 'python': [ 'autoload/weeget.py',
345 for language
in SCRIPT_EXTENSION
.keys():
347 autoloaded_files
= []
348 rootdir
= weechat
.info_get("weechat_dir", "") + os
.sep
+ language
349 for root
, dirs
, listfiles
in os
.walk(rootdir
):
351 files
[language
] = listfiles
352 elif root
== rootdir
+ os
.sep
+ "autoload":
353 autoloaded_files
= listfiles
354 for file in autoloaded_files
:
355 if file in files
[language
]:
356 files
[language
].remove(file)
357 files
[language
].insert(0, "autoload" + os
.sep
+ file)
360 def wg_get_local_scripts_status():
362 Return list of all local scripts with status (unknown/obsolete/running).
364 [ 'perl/weetris.pl': { 'unknown': '', 'obsolete': '1', 'running': '' },
365 'python/weeget.py': { 'unknown': '', 'obsolete': '', 'running': '1' }
368 local_scripts_status
= []
369 local_scripts
= wg_get_local_scripts()
370 if len(local_scripts
) > 0:
371 for language
, files
in local_scripts
.items():
373 script_status
= { "unknown": "", "obsolete": "", "running": "" }
374 name_with_ext
= os
.path
.basename(file)
375 script
= wg_search_script_by_name(os
.path
.basename(file))
377 script_status
["unknown"] = "1"
379 status
= wg_get_local_script_status(script
)
380 if status
["obsolete"]:
381 script_status
["obsolete"] = "1"
382 if wg_is_local_script_loaded(file):
383 script_status
["running"] = "1"
384 local_scripts_status
.append((language
+ os
.sep
+ file,
386 return local_scripts_status
388 def wg_search_scripts(search
):
389 """ Search word in scripts, return list of matching scripts. """
393 scripts_matching
= {}
394 for id, script
in wg_scripts
.items():
395 if script
["name"].lower().find(search
) >= 0 \
396 or script
["language"].lower().find(search
) >= 0 \
397 or script
["desc_en"].lower().find(search
) >= 0 \
398 or script
["desc_fr"].lower().find(search
) >= 0 \
399 or script
["tags"].lower().find(search
) >= 0:
400 scripts_matching
[id] = script
401 return scripts_matching
403 def wg_list_scripts(search
, installed
=False):
405 List all scripts (with optional search string).
406 If installed == True, then list only installed scripts.
407 For each script, display status (installed/running/new version available),
408 name of script, language and description.
410 ir buffers pl Sidebar with list of buffers.
411 i N go py Quick jump to buffers.
412 i weetris pl Tetris-like game.
415 search
= search
.strip().lower()
416 scripts_matching
= wg_search_scripts(search
)
417 if len(scripts_matching
) == 0:
418 weechat
.prnt("", "%s: no script found" % SCRIPT_NAME
)
423 weechat
.prnt("", "Scripts installed matching \"%s\":" % search
)
425 weechat
.prnt("", "Scripts for WeeChat %s matching \"%s\":"
426 % (weechat
.info_get("version", ""),
430 weechat
.prnt("", "Scripts installed:")
432 weechat
.prnt("", "Scripts for WeeChat %s:"
433 % weechat
.info_get("version", ""))
434 sorted_scripts
= sorted(scripts_matching
.items(),
435 key
=lambda s
: s
[1]["name"])
437 for item
in sorted_scripts
:
438 length
= len(item
[1]["name"])
439 if length
> length_max_name
:
440 length_max_name
= length
441 str_format
= "%%s%%s%%s%%s%%s%%s%%s %%s%%-%ds %%s%%-3s %%s%%s" \
443 for item
in sorted_scripts
:
448 status
= wg_get_local_script_status(script
)
449 if installed
and not status
["installed"]:
451 if status
["installed"]:
453 if status
["running"]:
455 if status
["obsolete"]:
457 weechat
.prnt("", str_format
458 % (wg_config_color("installed"),
460 wg_config_color("running"),
462 wg_config_color("obsolete"),
464 weechat
.color("chat"),
465 wg_config_color("script"),
467 wg_config_color("language"),
468 SCRIPT_EXTENSION
[script
["language"]],
469 weechat
.color("chat"),
472 def wg_show_script(name
):
474 Show detailed info about a script (in repository).
476 Script: weeget.py, version 0.7, license: GPL3
477 Author: Sebastien Helleu <flashcode [at] flashtux [dot] org>
478 Status: installed, running
479 Date: added: 2009-04-05, updated: 2009-09-07
480 URL: http://www.weechat.org/files/scripts/weeget.py
481 MD5: 4b0458dd5cc5c9a09ba8078f89830869
482 Desc: Scripts manager.
487 if len(wg_scripts
) == 0:
489 script
= wg_search_script_by_name(name
)
491 weechat
.prnt("", "%s: script \"%s%s%s\" not found"
493 wg_config_color("script"),
495 weechat
.color("chat")))
498 weechat
.prnt("", " Script: %s%s%s, version %s, license: %s"
499 % (wg_config_color("script"),
501 weechat
.color("chat"),
504 weechat
.prnt("", " Author: %s <%s>" % (script
["author"], script
["mail"]))
505 status
= wg_get_local_script_status(script
)
506 str_status
= "not installed"
507 if status
["installed"]:
508 str_status
= "installed"
509 if status
["running"]:
510 str_status
+= ", running"
512 str_status
+= ", not running"
513 if status
["obsolete"]:
514 str_status
+= " (new version available)"
515 weechat
.prnt("", " Status: %s" % str_status
)
516 date_added
= script
.get("added", "")[:10]
517 str_updated
= script
.get("updated", "")
518 if str_updated
!= "":
519 date_updated
= script
["updated"][:10]
520 if date_updated
== "0000-00-00" or date_updated
== date_added
:
522 if str_updated
!= "":
523 weechat
.prnt("", " Date: added: %s, updated: %s"
524 % (date_added
, date_updated
))
526 weechat
.prnt("", " Date: added: %s" % date_added
)
527 weechat
.prnt("", " URL: %s" % script
.get("url", ""))
528 weechat
.prnt("", " MD5: %s" % script
.get("md5sum", ""))
529 weechat
.prnt("", " Desc: %s" % script
.get("desc_en", ""))
530 weechat
.prnt("", " Tags: %s" % script
.get("tags", ""))
531 str_requires
= script
.get("requirements", "")
532 if str_requires
== "":
533 str_requires
= "(nothing)"
534 weechat
.prnt("", "Requires: %s" % str_requires
)
535 vmin
= script
.get("min_weechat", "")
536 vmax
= script
.get("max_weechat", "")
538 weechat
.prnt("", " Min: %s" % vmin
)
540 weechat
.prnt("", " Max: %s" % vmax
)
542 def wg_install_next_script():
544 Install first script in list wg_scripts_to_install and remove it from
547 global wg_scripts
, wg_scripts_to_install
, wg_current_script_install
548 global wg_hook_process
549 if len(wg_scripts
) == 0:
551 # be sure weeget is ALWAYS last script to install/update
552 # otherwise we'll lose end of list when weeget is unloaded by WeeChat
553 if SCRIPT_NAME
in wg_scripts_to_install
:
554 wg_scripts_to_install
.remove(SCRIPT_NAME
)
555 wg_scripts_to_install
.append(SCRIPT_NAME
)
556 # loop until a script is installed, or end if list is empty
557 while len(wg_scripts_to_install
) > 0:
558 name
= wg_scripts_to_install
.pop(0)
559 script
= wg_search_script_by_name(name
)
561 weechat
.prnt("", "%s: script \"%s%s%s\" not found"
563 wg_config_color("script"),
565 weechat
.color("chat")))
567 status
= wg_get_local_script_status(script
)
568 if status
["installed"] and not status
["obsolete"]:
570 "%s: script \"%s%s%s\" is already "
571 "installed and up to date"
573 wg_config_color("script"),
575 weechat
.color("chat")))
577 weechat
.prnt("", "%s: downloading \"%s%s%s\"..."
579 wg_config_color("script"),
581 weechat
.color("chat")))
582 if wg_hook_process
["script"] != "":
583 weechat
.unhook(wg_hook_process
["script"])
584 wg_hook_process
["script"] = ""
585 wg_current_script_install
= script
586 filename
= wg_config_get_dir() + os
.sep
+ script
["full_name"]
587 wg_hook_process
["script"] = wg_download_file(script
["url"], filename
, TIMEOUT_SCRIPT
,
588 "wg_process_script_cb", "")
589 # this function will be called again when script will be
593 def wg_install_scripts(names
):
594 """ Install scripts. """
595 global wg_scripts_to_install
596 for name
in names
.split(" "):
597 wg_scripts_to_install
.append(name
)
598 wg_install_next_script()
600 def wg_process_script_cb(data
, command
, rc
, stdout
, stderr
):
601 """ Callback when reading a script from website. """
602 global wg_hook_process
, wg_stdout
, wg_current_script_install
, wg_loaded_scripts
604 wg_stdout
["script"] += stdout
606 wg_stdout
["script"] += stderr
608 if wg_stdout
["script"].startswith("error:"):
609 weechat
.prnt("", "%s%s: error downloading script (%s)"
610 % (weechat
.prefix("error"), SCRIPT_NAME
,
611 wg_stdout
["update"][6:].strip()))
613 # ask C plugin to install/load script
614 weechat
.hook_signal_send(wg_current_script_install
["language"] + "_script_install",
615 weechat
.WEECHAT_HOOK_SIGNAL_STRING
,
616 wg_config_get_dir() + os
.sep
+ wg_current_script_install
["full_name"])
617 wg_hook_process
["script"] = ""
618 wg_install_next_script()
619 return weechat
.WEECHAT_RC_OK
621 def wg_check_scripts():
623 Check status of local script(s).
624 For each script found, display status (unknown/running/new version available).
626 r python/autoload/vdm.py
627 ?r python/autoload/dummy.py
631 local_scripts_status
= wg_get_local_scripts_status()
632 if len(local_scripts_status
) == 0:
635 weechat
.prnt("", "Local scripts:")
636 for file, status
in local_scripts_status
:
640 if status
["unknown"]:
642 if status
["running"]:
644 if status
["obsolete"]:
646 weechat
.prnt("", "%s%s%s%s%s%s%s %s%s%s%s"
647 % (wg_config_color("unknown"), str_unknown
,
648 wg_config_color("running"), str_running
,
649 wg_config_color("obsolete"), str_obsolete
,
650 weechat
.color("chat"),
651 os
.path
.dirname(file),
653 wg_config_color("script"),
654 os
.path
.basename(file)))
656 def wg_upgrade_scripts():
657 """ Upgrade scripts. """
658 global wg_scripts
, wg_scripts_to_install
659 if len(wg_scripts
) == 0:
661 scripts_to_upgrade
= []
662 for id, script
in wg_scripts
.items():
663 status
= wg_get_local_script_status(script
)
664 if status
["installed"] and status
["obsolete"]:
665 scripts_to_upgrade
.append(script
["name"])
666 if len(scripts_to_upgrade
) == 0:
667 weechat
.prnt("", "%s: all scripts are up to date" % SCRIPT_NAME
)
669 wg_scripts_to_install
.extend(scripts_to_upgrade
)
670 wg_install_next_script()
672 def wg_remove_scripts(names
):
673 """ Remove scripts. """
674 if len(wg_scripts
) == 0:
676 list_names
= names
.split(" ")
677 scripts_to_remove
= {}
678 for language
in SCRIPT_EXTENSION
.keys():
679 scripts_to_remove
[language
] = []
680 for name
in list_names
:
681 script
= wg_search_script_by_name(name
)
683 weechat
.prnt("", "%s: script \"%s%s%s\" not found"
685 wg_config_color("script"),
687 weechat
.color("chat")))
689 if script
["full_name"] not in scripts_to_remove
[script
["language"]]:
690 scripts_to_remove
[script
["language"]].append(script
["full_name"])
691 for language
in SCRIPT_EXTENSION
.keys():
692 if len(scripts_to_remove
[language
]) > 0:
693 # ask C plugin to remove script file(s)
694 weechat
.hook_signal_send(language
+ "_script_remove",
695 weechat
.WEECHAT_HOOK_SIGNAL_STRING
,
696 ",".join(scripts_to_remove
[language
]))
698 # ==================================[ xml ]===================================
700 def wg_execute_action():
701 """ Execute action. """
702 global wg_action
, wg_action_args
, wg_loaded_scripts
704 wg_get_loaded_scripts()
705 if wg_action
== "list":
706 wg_list_scripts(wg_action_args
)
707 elif wg_action
== "listinstalled":
708 wg_list_scripts(wg_action_args
, installed
=True)
709 elif wg_action
== "show":
710 wg_show_script(wg_action_args
)
711 elif wg_action
== "install":
712 wg_install_scripts(wg_action_args
)
713 elif wg_action
== "check":
715 elif wg_action
== "upgrade":
717 elif wg_action
== "remove":
718 wg_remove_scripts(wg_action_args
)
720 weechat
.prnt("", "%s%s: unknown action \"%s\""
721 % (weechat
.prefix("error"), SCRIPT_NAME
, wg_action
))
726 wg_loaded_scripts
= {}
728 def wg_check_version(script
):
729 """ Check if a script is designed for current running WeeChat version."""
730 version
= weechat
.info_get("version", "")
731 version
= version
.split("-", 1)[0]
732 vmin
= script
.get("min_weechat", "")
733 vmax
= script
.get("max_weechat", "")
734 if vmin
!= "" and version
< vmin
:
736 if vmax
!= "" and version
> vmax
:
742 Parse XML scripts list and return dictionary with list, with key 'id'.
743 Example of item return in dictionary :
744 '119': { 'name' : 'weeget',
746 'url' : 'http://www.weechat.org/files/scripts/weeget.py',
747 'language' : 'python',
749 'md5sum' : 'd500714fc19b0e10cc4e339e70739e4ad500714fc19b0e10cc4e339e70739e4a',
751 'desc_en' : 'Scripts manager.',
752 'desc_fr' : 'Gestionnaire de scripts.',
753 'requirements': 'python 2.5',
754 'min_weechat' : '0.3.0',
756 'author' : 'FlashCode',
757 'mail' : 'flashcode [at] flashtux [dot] org',
758 'added' : '2009-04-05 22:39:18',
759 'updated' : '0000-00-00 00:00:00' }
761 global wg_scripts
, wg_action
, wg_action_args
764 f
= gzip
.open(wg_config_get_cache_filename(), "rb")
768 weechat
.prnt("", "%s%s: unable to read xml file"
769 % (weechat
.prefix("error"), SCRIPT_NAME
))
772 dom
= xml
.dom
.minidom
.parseString(string
)
775 "%s%s: unable to parse xml list of scripts"
776 % (weechat
.prefix("error"), SCRIPT_NAME
))
781 for scriptNode
in dom
.getElementsByTagName("plugin"):
782 id = scriptNode
.getAttribute("id")
784 for node
in scriptNode
.childNodes
:
785 if node
.nodeType
== node
.ELEMENT_NODE
:
786 if node
.firstChild
!= None:
787 nodename
= node
.nodeName
788 value
= node
.firstChild
.data
789 if sys
.version_info
< (3,):
790 # python 2.x: convert unicode to str (in python 3.x, id and text are already strings)
791 nodename
= nodename
.encode("utf-8")
792 value
= value
.encode("utf-8")
793 script
[nodename
] = value
794 if script
["language"] in SCRIPT_EXTENSION
:
795 script
["full_name"] = script
["name"] + "." + SCRIPT_EXTENSION
[script
["language"]]
796 if wg_check_version(script
):
797 wg_scripts
[id] = script
800 def wg_process_update_cb(data
, command
, rc
, stdout
, stderr
):
801 """ Callback when reading XML cache file from website. """
802 global wg_hook_process
, wg_stdout
, wg_scripts
804 wg_stdout
["update"] += stdout
806 wg_stdout
["update"] += stderr
808 if wg_stdout
["update"].startswith("error:"):
809 weechat
.prnt("", "%s%s: error downloading scripts (%s)"
810 % (weechat
.prefix("error"), SCRIPT_NAME
,
811 wg_stdout
["update"][6:].strip()))
813 weechat
.prnt("", "%s: scripts downloaded" % SCRIPT_NAME
)
815 wg_hook_process
["update"] = ""
816 return weechat
.WEECHAT_RC_OK
818 def wg_update_cache():
819 """ Download list of scripts and update local cache. """
820 global wg_config_option
, wg_hook_process
, wg_stdout
821 # get data from website, via hook_process
822 if wg_hook_process
["update"] != "":
823 weechat
.unhook(wg_hook_process
["update"])
824 wg_hook_process
["update"] = ""
825 weechat
.prnt("", "%s: downloading list of scripts..." % SCRIPT_NAME
)
826 wg_stdout
["update"] = ""
827 wg_config_create_dir()
828 url
= weechat
.config_string(wg_config_option
["scripts_url"])
829 filename
= wg_config_get_cache_filename()
830 wg_hook_process
["update"] = wg_download_file(url
, filename
, TIMEOUT_UPDATE
,
831 "wg_process_update_cb", "")
833 def wg_read_scripts(download_list
=True):
834 """ Read scripts list (download list if needed and asked). """
836 cache_file
= wg_config_get_cache_filename()
837 if os
.path
.isfile(cache_file
):
838 # check if local cache file is too old
839 cache_expire
= weechat
.config_integer(wg_config_option
["scripts_cache_expire"]) * 60
840 if cache_expire
>= 0:
841 diff_time
= time
.time() - os
.stat(cache_file
)[stat
.ST_MTIME
]
842 if download_list
and diff_time
>= cache_expire
:
843 os
.unlink(cache_file
)
845 if len(wg_scripts
) > 0:
848 if os
.path
.isfile(cache_file
):
853 # ================================[ command ]=================================
855 def wg_cmd(data
, buffer, args
):
856 """ Callback for /weeget command. """
857 global wg_action
, wg_action_args
859 weechat
.command("", "/help %s" % SCRIPT_COMMAND
)
860 return weechat
.WEECHAT_RC_OK
861 argv
= args
.strip().split(" ", 1)
863 return weechat
.WEECHAT_RC_OK
870 if argv
[0] == "show" or \
871 argv
[0] == "install" or \
873 weechat
.prnt("", "%s: too few arguments for action \"%s\""
874 % (SCRIPT_NAME
, argv
[0]))
875 return weechat
.WEECHAT_RC_OK
877 # execute asked action
878 if argv
[0] == "update":
884 wg_action_args
= argv
[1]
887 return weechat
.WEECHAT_RC_OK
889 def wg_completion_scripts_cb(data
, completion_item
, buffer, completion
):
890 """ Complete with known script names, for command '/weeget'. """
892 wg_read_scripts(download_list
=False)
893 if len(wg_scripts
) > 0:
894 for id, script
in wg_scripts
.items():
895 weechat
.hook_completion_list_add(completion
, script
["full_name"],
896 0, weechat
.WEECHAT_LIST_POS_SORT
)
897 return weechat
.WEECHAT_RC_OK
899 def wg_completion_scripts_installed_cb(data
, completion_item
, buffer, completion
):
900 """ Complete with names of scripts installed, for command '/weeget'. """
902 wg_read_scripts(download_list
=False)
903 if len(wg_scripts
) > 0:
904 for id, script
in wg_scripts
.items():
905 status
= wg_get_local_script_status(script
)
906 if status
["installed"]:
907 weechat
.hook_completion_list_add(completion
, script
["full_name"],
908 0, weechat
.WEECHAT_LIST_POS_SORT
)
909 return weechat
.WEECHAT_RC_OK
911 def wg_completion_scripts_tags_cb(data
, completion_item
, buffer, completion
):
912 """ Complete with known tags, for command '/weeget'. """
914 wg_read_scripts(download_list
=False)
915 if len(wg_scripts
) > 0:
916 for id, script
in wg_scripts
.items():
918 for tag
in script
["tags"].split(","):
919 weechat
.hook_completion_list_add(completion
, tag
,
920 0, weechat
.WEECHAT_LIST_POS_SORT
)
921 return weechat
.WEECHAT_RC_OK
923 # ==================================[ main ]==================================
925 if __name__
== "__main__" and import_ok
:
926 if weechat
.register(SCRIPT_NAME
, SCRIPT_AUTHOR
, SCRIPT_VERSION
, SCRIPT_LICENSE
,
927 SCRIPT_DESC
, "wg_unload_script", ""):
930 if weechat
.config_string(wg_config_option
["scripts_url"]).find("weechat.flashtux.org") >= 0:
931 weechat
.prnt("", "%sWarning: old site still used in URL for plugins.xml.gz, you should do: /unset wg.scripts.url"
932 % weechat
.prefix("error"))
933 str_installed
= wg_config_color("installed") + "i" + weechat
.color("chat")
934 str_unknown
= wg_config_color("unknown") + "?" + weechat
.color("chat")
935 str_running
= wg_config_color("running") + "r" + weechat
.color("chat")
936 str_obsolete
= wg_config_color("obsolete") + "N" + weechat
.color("chat")
937 weechat
.hook_command(SCRIPT_COMMAND
,
938 "WeeChat scripts manager",
939 "list|listinstalled [<text>|<tag>] || show <script>"
940 " || install|remove <script> [<script>...] || check|update|upgrade",
941 " list: list scripts (search text if given)\n"
942 "listinstalled: list installed scripts (search text if given)\n"
943 " show: show detailed information about a script (in repository)\n"
944 " install: install/upgrade script(s)\n"
945 " check: check if local scripts needs upgrade\n"
946 " update: update local scripts cache\n"
947 " upgrade: upgrade all local scripts if they are obsolete\n"
948 " remove: remove script(s)\n\n"
949 "Indicators in lists (first column):\n"
950 " " + str_installed
+ " script is installed\n"
951 " " + str_unknown
+ " unknown script\n"
952 " " + str_running
+ " script is running (loaded)\n"
953 " " + str_obsolete
+ " script is obsolete (new version available)\n\n"
955 " /" + SCRIPT_COMMAND
+ " list => list all scripts\n"
956 " /" + SCRIPT_COMMAND
+ " list game => list all scripts with text/tag \"game\"\n"
957 " /" + SCRIPT_COMMAND
+ " install beep.pl => install script beep.pl\n"
958 " /" + SCRIPT_COMMAND
+ " remove beep.pl => remove script beep.pl",
959 "list %(weeget_scripts_tags)"
960 " || listinstalled %(weeget_scripts_tags)"
961 " || show %(weeget_scripts)"
962 " || install %(weeget_scripts)|%*"
963 " || remove %(weeget_scripts_installed)|%*"
968 weechat
.hook_completion("weeget_scripts", "list of scripts in repository",
969 "wg_completion_scripts_cb", "")
970 weechat
.hook_completion("weeget_scripts_installed", "list of scripts installed",
971 "wg_completion_scripts_installed_cb", "")
972 weechat
.hook_completion("weeget_scripts_tags", "tags of scripts in repository",
973 "wg_completion_scripts_tags_cb", "")
975 # ==================================[ end ]===================================
977 def wg_unload_script():
978 """ Function called when script is unloaded. """
980 return weechat
.WEECHAT_RC_OK