├── .gitmodules
├── .gitignore
├── docker
├── running.png
├── stopped.png
└── __init__.py
├── .archive
├── rand
│ ├── rand.png
│ └── __init__.py
├── xkcd
│ ├── image.png
│ ├── misc
│ │ └── demo.gif
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── install-plugin.sh
│ └── __init__.py
├── packagist
│ ├── logo.png
│ └── __init__.py
├── dango_emoji
│ ├── dangoemoji.png
│ └── __init__.py
├── googletrans
│ ├── google_translate.png
│ └── __init__.py
├── README.md
├── lpass
│ ├── lastpass.svg
│ └── __init__.py
├── youtube
│ └── youtube.svg
├── binance
│ ├── Binance.svg
│ └── __init__.py
├── bitfinex
│ ├── Bitfinex.svg
│ └── __init__.py
├── npm
│ ├── logo.svg
│ └── __init__.py
├── gnome_dictionary
│ └── __init__.py
├── php_eval
│ ├── __init__.py
│ └── php.svg
├── fortune
│ └── __init__.py
├── node_eval
│ └── __init__.py
├── ip
│ └── __init__.py
├── google_translate
│ └── __init__.py
├── atom_projects
│ └── __init__.py
├── dango_kao
│ ├── __init__.py
│ └── kaoicon.svg
├── texdoc
│ └── __init__.py
├── vpn
│ └── __init__.py
├── find
│ └── __init__.py
├── window_switcher
│ └── __init__.py
├── mathematica_eval
│ └── __init__.py
├── base_converter
│ └── __init__.py
├── inhibit_sleep
│ └── __init__.py
├── units
│ └── __init__.py
├── gnote
│ └── __init__.py
├── tomboy
│ └── __init__.py
├── pidgin
│ └── __init__.py
├── currency_converter
│ └── __init__.py
├── scrot
│ └── __init__.py
├── unicode_emoji
│ └── __init__.py
├── multi_google_translate
│ └── __init__.py
└── timer
│ └── __init__.py
├── coingecko
└── coingecko.png
├── wikipedia
└── wikipedia.png
├── translators
└── google_translate.png
├── README.md
├── dice_roll
└── icons
│ ├── d4.svg
│ ├── d8.svg
│ ├── d6.svg
│ ├── d2.svg
│ ├── d10.svg
│ ├── d12.svg
│ ├── d20.svg
│ ├── dice.svg
│ └── d100.svg
├── unit_converter
└── icons
│ ├── time.svg
│ ├── mass.svg
│ ├── unit_converter.svg
│ ├── current.svg
│ ├── printing_unit.svg
│ ├── temperature.svg
│ ├── length.svg
│ ├── luminosity.svg
│ ├── lengthtime.svg
│ ├── substance.svg
│ └── currency.svg
├── jetbrains_projects
└── icons
│ ├── writerside.svg
│ ├── webstorm.svg
│ ├── rider.svg
│ ├── clion.svg
│ ├── rubymine.svg
│ ├── goland.svg
│ ├── phpstorm.svg
│ ├── idea.svg
│ ├── datagrip.svg
│ ├── dataspell.svg
│ ├── pycharm.svg
│ └── androidstudio.svg
├── CONTRIBUTING.md
├── .github
└── workflows
│ ├── telegram_notify_issues.yml
│ └── telegram_notify_comments.yml
├── bitwarden
└── bw.svg
├── syncthing
├── syncthing_active.svg
└── syncthing_inactive.svg
├── zeal
└── __init__.py
├── python_eval
├── __init__.py
└── python.svg
├── duckduckgo
└── __init__.py
├── pomodoro
└── pomodoro.svg
├── goldendict
└── __init__.py
├── aur
└── arch.svg
├── arch_wiki
├── arch.svg
└── __init__.py
├── pacman
├── arch.svg
└── __init__.py
├── tex_to_unicode
├── tex.svg
└── __init__.py
├── color
└── __init__.py
├── locate
└── __init__.py
├── copyq
└── __init__.py
├── kill
└── __init__.py
├── x_window_switcher
└── __init__.py
├── vscode_projects
└── icon.svg
└── virtualbox
└── __init__.py
/.gitmodules:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | /.idea
3 | /.vscode
4 | /.venv
5 | albert.pyi
--------------------------------------------------------------------------------
/docker/running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/docker/running.png
--------------------------------------------------------------------------------
/docker/stopped.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/docker/stopped.png
--------------------------------------------------------------------------------
/.archive/rand/rand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/rand/rand.png
--------------------------------------------------------------------------------
/.archive/xkcd/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/xkcd/image.png
--------------------------------------------------------------------------------
/coingecko/coingecko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/coingecko/coingecko.png
--------------------------------------------------------------------------------
/wikipedia/wikipedia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/wikipedia/wikipedia.png
--------------------------------------------------------------------------------
/.archive/packagist/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/packagist/logo.png
--------------------------------------------------------------------------------
/.archive/xkcd/misc/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/xkcd/misc/demo.gif
--------------------------------------------------------------------------------
/translators/google_translate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/translators/google_translate.png
--------------------------------------------------------------------------------
/.archive/dango_emoji/dangoemoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/dango_emoji/dangoemoji.png
--------------------------------------------------------------------------------
/.archive/googletrans/google_translate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertlauncher/python/HEAD/.archive/googletrans/google_translate.png
--------------------------------------------------------------------------------
/.archive/README.md:
--------------------------------------------------------------------------------
1 | # The archive
2 |
3 | 0.18 changed the API and most of the plugins here have no maintainer. Please volunteer as maintainer and help getting plugins shipped. 👍
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚠️ ARCHIVED ⚠️
2 |
3 | The plugins in this repository have been moved to [dedicated repositories](https://github.com/orgs/albertlauncher/repositories) using `history_to_submodules.sh`.
4 |
--------------------------------------------------------------------------------
/dice_roll/icons/d4.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unit_converter/icons/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/lpass/lastpass.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/.archive/youtube/youtube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unit_converter/icons/mass.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dice_roll/icons/d8.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dice_roll/icons/d6.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dice_roll/icons/d2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/binance/Binance.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/dice_roll/icons/d10.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dice_roll/icons/d12.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/bitfinex/Bitfinex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/writerside.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to contribute to this repository
2 |
3 | ### Do you have an issue?
4 |
5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/albertlauncher/albert/issues).
6 | * Create a new issue using the templates provided.
7 | * Ping the authors of the related plugin.
8 |
9 | ### Do you want to contribute code?
10 |
11 | * Add a copyright notice, otherwise the code is in public domain.
12 | * You agree to publish your contribution under the MIT license.
13 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
14 | * Changes that do not add anything substantial to the stability, functionality, or testability will generally not be accepted.
15 |
16 | Thanks! :heart:
17 |
--------------------------------------------------------------------------------
/.github/workflows/telegram_notify_issues.yml:
--------------------------------------------------------------------------------
1 | name: Telegram Notifications
2 |
3 | on:
4 | issues:
5 | types: [opened, reopened]
6 |
7 | jobs:
8 | notify:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Send notifications to Telegram
14 | run: >
15 | curl -s
16 | -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_NOTIFIER_BOT_TOKEN }}/sendMessage
17 | -d chat_id=${{ secrets.TELEGRAM_ALBERT_CHAT_ID }}
18 | -d text="${MESSAGE}"
19 | -d parse_mode=HTML
20 | -d disable_web_page_preview=true
21 | >> /dev/null
22 | env:
23 | MESSAGE: "New issue:%0A${{ github.event.repository.name }}#${{ github.event.issue.number }}: ${{ github.event.issue.title }}"
24 |
25 |
--------------------------------------------------------------------------------
/.github/workflows/telegram_notify_comments.yml:
--------------------------------------------------------------------------------
1 | name: Telegram Notifications
2 |
3 | on:
4 |
5 | issue_comment:
6 | types: [created]
7 |
8 | jobs:
9 | notify:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Send notifications to Telegram
15 | run: >
16 | curl -s
17 | -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_NOTIFIER_BOT_TOKEN }}/sendMessage
18 | -d chat_id=${{ secrets.TELEGRAM_ALBERT_CHAT_ID }}
19 | -d text="${MESSAGE}"
20 | -d parse_mode=HTML
21 | -d disable_web_page_preview=true
22 | >> /dev/null
23 | env:
24 | MESSAGE: "${{ github.event.comment.user.login }} on ${{ github.event.repository.name }}#${{ github.event.issue.number }}: ${{ github.event.issue.title }}%0A${{ github.event.comment.body }}"
25 |
26 |
--------------------------------------------------------------------------------
/unit_converter/icons/unit_converter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bitwarden/bw.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/.archive/npm/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
18 |
--------------------------------------------------------------------------------
/.archive/gnome_dictionary/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Gnome dictionary.
4 |
5 | Needs 'gnome-dictionary' to be already installed.
6 |
7 | Sysnopsis: """
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | from subprocess import run
12 |
13 | from albert import *
14 |
15 | __title__ = "Gnome Dictionary"
16 | __version__ = "0.4.0"
17 | __triggers__ = "def "
18 | __authors__ = "Nikhil Wanpal"
19 | __exec_deps__ = ["gnome-dictionary"]
20 |
21 | iconPath = iconLookup('accessories-dictionary')
22 |
23 |
24 | def handleQuery(query):
25 | if query.isTriggered:
26 | return Item(id=__title__,
27 | icon=iconPath,
28 | text=__title__,
29 | subtext="Search for '%s' using %s" % (query.string, __title__),
30 | actions=[ProcAction("Opens %s and searches for '%s'" % (__title__, query.string),
31 | ["gnome-dictionary", "--look-up=%s" % query.string])])
32 |
--------------------------------------------------------------------------------
/dice_roll/icons/d20.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/xkcd/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ######################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 | *.pyc
10 |
11 | # Packages #
12 | ######################
13 | # it's better to unpack these files and commit the raw source
14 | # git has its own built in compression methods
15 | *.7z
16 | *.dmg
17 | *.gz
18 | *.iso
19 | *.jar
20 | *.rar
21 | *.tar
22 | *.zip
23 |
24 | # Logs and databases #
25 | ######################
26 | *.log
27 | *.sql
28 | *.sqlite
29 |
30 | # OS generated files #
31 | ######################
32 | .DS_Store
33 | .DS_Store?
34 | ._*
35 | .Spotlight-V100
36 | .Trashes
37 | ehthumbs.db
38 | Thumbs.db
39 |
40 | tags
41 |
42 | _build
43 |
44 | # CMake
45 | build*
46 | documentation
47 | *.user*
48 | log
49 | *.dir*
50 | *.a
51 | *.make
52 | doc/html*
53 | doc/latex*
54 |
55 | # Python
56 | .mypy_cache
57 | # Python - Coverage
58 | .coverage
59 | coverage.xml
60 | htmlcov/
61 |
62 | # Vim
63 | Session.vim
64 | .netrwhist
65 | *~
66 | tags
67 | .projections.json
68 | compile_commands.json
69 |
70 |
71 | # backup files - created during sed operations
72 | *.bak
73 |
--------------------------------------------------------------------------------
/.archive/xkcd/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Nikos Koukis
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/.archive/php_eval/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Evaluate simple PHP expressions. Use it with care every keystroke triggers an evaluation."""
4 |
5 | # Copyright (c) 2022 Manuel Schneider
6 |
7 | import os
8 | import subprocess
9 | from albert import *
10 |
11 | __title__ = 'PHP Eval'
12 | __version__ = '0.4.0'
13 | __triggers__ = 'php '
14 | __authors__ = 'Hammed Oyedele'
15 | __exec_deps__ = ['php']
16 |
17 | iconPath = os.path.dirname(__file__) + '/php.svg'
18 |
19 | def run(exp):
20 | return subprocess.getoutput('php -r "%s"' % exp.replace('"', '\\"'))
21 |
22 |
23 | def handleQuery(query):
24 | if query.isTriggered:
25 | item = Item(
26 | id=__title__,
27 | icon=iconPath
28 | )
29 | stripped = query.string.strip()
30 |
31 | if stripped == '':
32 | item.text = 'Enter a PHP expression...'
33 | else:
34 | item.text = run('echo %s;' % stripped)
35 | item.subtext = run('echo gettype(%s);' % stripped)
36 | item.addAction(ClipAction('Copy result to clipboard', item.text))
37 |
38 | return item
39 |
--------------------------------------------------------------------------------
/dice_roll/icons/dice.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/syncthing/syncthing_active.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/syncthing/syncthing_inactive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/fortune/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Display random poignant, inspirational, silly or snide phrase.
4 |
5 | Fortune wrapper extension.
6 |
7 | Synopsis: """
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | import subprocess as sp
12 | from albert import *
13 |
14 | __title__ = "Fortune"
15 | __version__ = "0.4.0"
16 | __triggers__ = "fortune"
17 | __authors__ = "Kelvin Wong"
18 | __exec_deps__ = ["fortune"]
19 |
20 | iconPath = iconLookup("font")
21 |
22 |
23 | def handleQuery(query):
24 | if query.isTriggered:
25 | newFortune = generateFortune()
26 | if newFortune is not None:
27 | return getFortuneItem(query, newFortune)
28 |
29 |
30 | def generateFortune():
31 | try:
32 | return sp.check_output(["fortune", "-s"]).decode().strip()
33 | except sp.CalledProcessError as e:
34 | return None
35 |
36 |
37 | def getFortuneItem(query, fortune):
38 | return Item(
39 | id=__title__,
40 | icon=iconPath,
41 | text=fortune,
42 | subtext="Copy this random, hopefully interesting, adage",
43 | actions=[ClipAction("Copy to clipboard", fortune)]
44 | )
45 |
--------------------------------------------------------------------------------
/.archive/node_eval/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Evaluate simple JavaScript expressions. Use it with care every keystroke triggers an evaluation."""
4 |
5 | # Copyright (c) 2022 Manuel Schneider
6 |
7 | import os
8 | import subprocess
9 | from albert import *
10 |
11 | __title__ = 'Node Eval'
12 | __version__ = '0.4.0'
13 | __triggers__ = 'node '
14 | __authors__ = 'Hammed Oyedele'
15 | __exec_deps__ = ['node']
16 |
17 | iconPath = os.path.dirname(__file__) + '/nodejs.svg'
18 |
19 |
20 | def run(exp):
21 | return subprocess.getoutput('node --print "%s"' % exp.replace('"', '\\"'))
22 |
23 |
24 | def handleQuery(query):
25 | if query.isTriggered:
26 | item = Item(
27 | id=__title__,
28 | icon=iconPath
29 | )
30 | stripped = query.string.strip()
31 |
32 | if stripped == '':
33 | item.text = 'Enter a JavaScript expression...'
34 | else:
35 | item.text = run(stripped)
36 | item.subtext = run(
37 | 'Object.prototype.toString.call(%s).slice(8, -1).toLowerCase()' % stripped)
38 | item.addAction(ClipAction('Copy result to clipboard', item.text))
39 |
40 | return item
41 |
--------------------------------------------------------------------------------
/dice_roll/icons/d100.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unit_converter/icons/current.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/unit_converter/icons/printing_unit.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/webstorm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/rider.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/ip/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Get internal and external IP address.
4 |
5 | Synopsis: """
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import socket
10 | from urllib import request
11 |
12 | from albert import *
13 |
14 | __title__ = "IP Addresses"
15 | __version__ = "0.4.0"
16 | __triggers__ = "ip "
17 | __authors__ = ["Manuel S.", "Benedict Dudel"]
18 |
19 | iconPath = iconLookup("preferences-system-network")
20 |
21 |
22 | def handleQuery(query):
23 | if not query.isTriggered:
24 | return None
25 |
26 | with request.urlopen("https://ipecho.net/plain") as response:
27 | externalIP = response.read().decode()
28 |
29 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
30 | s.connect(("10.255.255.255", 1))
31 | internalIP = s.getsockname()[0]
32 | s.close()
33 |
34 | items = []
35 | if externalIP:
36 | items.append(Item(
37 | id = __title__,
38 | icon = iconPath,
39 | text = externalIP,
40 | subtext = "Your external ip address from ipecho.net",
41 | actions = [ClipAction("Copy ip address to clipboard", externalIP)]
42 | ))
43 |
44 | if internalIP:
45 | items.append(Item(
46 | id = __title__,
47 | icon = iconPath,
48 | text = internalIP,
49 | subtext = "Your internal ip address",
50 | actions = [ClipAction("Copy ip address to clipboard", internalIP)]
51 | ))
52 |
53 | return items
54 |
--------------------------------------------------------------------------------
/zeal/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | import albert
5 |
6 | md_iid = "3.0"
7 | md_version = "3.0"
8 | md_name = "Zeal"
9 | md_description = "Search in Zeal docs"
10 | md_license = "MIT"
11 | md_url = "https://github.com/albertlauncher/python/tree/main/zeal"
12 | md_authors = "@manuelschneid3r"
13 | md_bin_dependencies = ['zeal']
14 |
15 | def createItem(query: str):
16 | return albert.StandardItem(
17 | id=md_name,
18 | text=md_name,
19 | subtext=f"Search '{query}' in Zeal",
20 | iconUrls=["xdg:zeal"],
21 | actions=[albert.Action("zeal", "Search in Zeal",
22 | lambda q=query: albert.runDetachedProcess(['zeal', q]))]
23 | )
24 |
25 | class FBH(albert.FallbackHandler):
26 |
27 | def id(self):
28 | return "zeal_fbh"
29 |
30 | def name(self):
31 | return md_name
32 |
33 | def description(self):
34 | return md_description
35 |
36 | def fallbacks(self, s):
37 | return [createItem(s)] if s else []
38 |
39 |
40 | class Plugin(albert.PluginInstance, albert.TriggerQueryHandler):
41 |
42 | def __init__(self):
43 | albert.PluginInstance.__init__(self)
44 | albert.TriggerQueryHandler.__init__(self)
45 | self.fbh = FBH()
46 |
47 | def defaultTrigger(self):
48 | return "z "
49 |
50 | def extensions(self):
51 | return [self, self.fbh]
52 |
53 | def handleTriggerQuery(self, query):
54 | if stripped := query.string.strip():
55 | query.add(createItem(stripped))
56 |
--------------------------------------------------------------------------------
/.archive/php_eval/php.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/python_eval/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2017-2014 Manuel Schneider
3 |
4 | from pathlib import Path
5 |
6 | from albert import *
7 |
8 | md_iid = "3.0"
9 | md_version = "2.0"
10 | md_name = "Python Eval"
11 | md_description = "Evaluate Python code"
12 | md_license = "BSD-3"
13 | md_url = "https://github.com/albertlauncher/python/tree/main/python_eval"
14 | md_authors = "@manuelschneid3r"
15 |
16 |
17 | class Plugin(PluginInstance, TriggerQueryHandler):
18 |
19 | def __init__(self):
20 | PluginInstance.__init__(self)
21 | TriggerQueryHandler.__init__(self)
22 | self.iconUrls = [f"file:{Path(__file__).parent}/python.svg"]
23 |
24 | def synopsis(self, query):
25 | return ""
26 |
27 | def defaultTrigger(self):
28 | return "py "
29 |
30 | def handleTriggerQuery(self, query):
31 | stripped = query.string.strip()
32 | if stripped:
33 | try:
34 | result = eval(stripped)
35 | except Exception as ex:
36 | result = ex
37 |
38 | result_str = str(result)
39 |
40 | query.add(StandardItem(
41 | id=self.id(),
42 | text=result_str,
43 | subtext=type(result).__name__,
44 | inputActionText=query.trigger + result_str,
45 | iconUrls=self.iconUrls,
46 | actions = [
47 | Action("copy", "Copy result to clipboard", lambda r=result_str: setClipboardText(r)),
48 | Action("exec", "Execute python code", lambda r=result_str: exec(stripped)),
49 | ]
50 | ))
51 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/clion.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/python_eval/python.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unit_converter/icons/temperature.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/unit_converter/icons/length.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/duckduckgo/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | """
5 | Inline DuckDuckGo web search using the 'duckduckgo-search' library.
6 | """
7 |
8 | from albert import *
9 | from pathlib import Path
10 | from duckduckgo_search import DDGS
11 | from itertools import islice
12 | from time import sleep
13 |
14 | md_iid = "3.0"
15 | md_version = "2.0"
16 | md_name = 'DuckDuckGo'
17 | md_description = 'Inline DuckDuckGo web search'
18 | md_license = "MIT"
19 | md_url = 'https://github.com/albertlauncher/python/tree/main/duckduckgo'
20 | md_lib_dependencies = "duckduckgo-search"
21 | md_authors = "@manuelschneid3r"
22 |
23 |
24 | class Plugin(PluginInstance, TriggerQueryHandler):
25 |
26 | def __init__(self):
27 | PluginInstance.__init__(self)
28 | TriggerQueryHandler.__init__(self)
29 | self.ddg = DDGS()
30 | self.iconUrls = [f"file:{Path(__file__).parent}/duckduckgo.svg"]
31 |
32 | def defaultTrigger(self):
33 | return "ddg "
34 |
35 | def handleTriggerQuery(self, query):
36 |
37 | stripped = query.string.strip()
38 | if stripped:
39 |
40 | # dont flood
41 | for _ in range(25):
42 | sleep(0.01)
43 | if not query.isValid:
44 | return
45 |
46 | for r in islice(self.ddg.text(stripped, safesearch='off'), 10):
47 | query.add(
48 | StandardItem(
49 | id=self.id(),
50 | text=r['title'],
51 | subtext=r['body'],
52 | iconUrls=self.iconUrls,
53 | actions=[Action("open", "Open link", lambda u=r['href']: openUrl(u))]
54 | )
55 | )
56 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/rubymine.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/google_translate/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Translate text using Google Translate.
4 |
5 | Check available languages here: https://cloud.google.com/translate/docs/languages
6 |
7 | Synopsis: """
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | import json
12 | import urllib.parse
13 | import urllib.request
14 |
15 | from albert import *
16 |
17 | __title__ = "Google Translate"
18 | __version__ = "0.4.0"
19 | __triggers__ = "tr "
20 | __authors__ = "Manuel S."
21 |
22 | ua = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36"
23 | urltmpl = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=%s&tl=%s&dt=t&q=%s"
24 |
25 | iconPath = iconLookup('config-language') or ":python_module"
26 |
27 | def handleQuery(query):
28 | if query.isTriggered:
29 | fields = query.string.split()
30 | item = Item(id=__title__, icon=iconPath)
31 | if len(fields) >= 3:
32 | src = fields[0]
33 | dst = fields[1]
34 | txt = " ".join(fields[2:])
35 | url = urltmpl % (src, dst, urllib.parse.quote_plus(txt))
36 | req = urllib.request.Request(url, headers={'User-Agent': ua})
37 | with urllib.request.urlopen(req) as response:
38 | data = json.loads(response.read().decode('utf-8'))
39 | result = data[0][0][0]
40 | item.text = result
41 | item.subtext = "%s-%s translation of %s" % (src.upper(), dst.upper(), txt)
42 | item.addAction(ClipAction("Copy translation to clipboard", result))
43 | return item
44 | else:
45 | item.text = __title__
46 | item.subtext = "Enter a query in the form of \"<srclang> <dstlang> <text>\""
47 | return item
48 |
--------------------------------------------------------------------------------
/pomodoro/pomodoro.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/unit_converter/icons/luminosity.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/.archive/atom_projects/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """List and open your Atom projects.
4 |
5 | Synopsis: [filter]"""
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import os
10 | import re
11 | import time
12 | from pathlib import Path
13 |
14 | import cson
15 |
16 | from albert import *
17 |
18 | __title__ = "Atom Projects"
19 | __version__ = "0.4.0"
20 | __triggers__ = "atom "
21 | __authors__ = "Manuel S."
22 | __exec_deps__ = ["atom"]
23 | __py_deps__ = ["cson"]
24 |
25 | projects_file = str(Path.home()) + "/.atom/projects.cson"
26 | iconPath = iconLookup('atom')
27 | mtime = 0
28 | projects = []
29 |
30 |
31 | def updateProjects():
32 | global mtime
33 | try:
34 | new_mtime = os.path.getmtime(projects_file)
35 | except Exception as e:
36 | warning("Could not get mtime of file: " + projects_file + str(e))
37 | if mtime != new_mtime:
38 | mtime = new_mtime
39 | with open(projects_file) as projects_cson:
40 | global projects
41 | projects = cson.loads(projects_cson.read())
42 |
43 |
44 | def handleQuery(query):
45 | if not query.isTriggered:
46 | return
47 |
48 | updateProjects()
49 |
50 | stripped = query.string.strip()
51 |
52 | items = []
53 | for project in projects:
54 | if re.search(stripped, project['title'], re.IGNORECASE):
55 | items.append(Item(id=__title__ + project['title'],
56 | icon=iconPath,
57 | text=project['title'],
58 | subtext="Group: %s" % (project['group'] if 'group' in project else "None"),
59 | actions=[
60 | ProcAction(text="Open project in Atom",
61 | commandline=["atom"] + project['paths'])
62 | ]))
63 | return items
64 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/goland.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/goldendict/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2017-2024 Manuel Schneider
3 |
4 | import os
5 | import shutil
6 |
7 | from albert import *
8 |
9 | md_iid = "3.0"
10 | md_version = "2.0"
11 | md_name = "GoldenDict"
12 | md_description = "Quick access to GoldenDict"
13 | md_license = "MIT"
14 | md_url = "https://github.com/albertlauncher/python/tree/main/goldendict"
15 | md_authors = "@manuelschneid3r"
16 |
17 |
18 | class Plugin(PluginInstance, TriggerQueryHandler):
19 |
20 | def __init__(self):
21 | PluginInstance.__init__(self)
22 | TriggerQueryHandler.__init__(self)
23 |
24 | commands = [
25 | '/var/lib/flatpak/exports/bin/org.goldendict.GoldenDict', # flatpak
26 | '/var/lib/flatpak/exports/bin/io.github.xiaoyifang.goldendict_ng', # flatpak ng
27 | 'goldendict', # native
28 | 'goldendict-ng', # native ng
29 | ]
30 |
31 | executables = [e for e in [shutil.which(c) for c in commands] if e]
32 |
33 | if not executables:
34 | raise RuntimeError(f'None of the GoldenDict distributions found.')
35 |
36 | self.executable = executables[0]
37 | self.iconUrls = [f'xdg:{os.path.basename(self.executable)}']
38 |
39 | if len(executables) > 1:
40 | warning(f"Multiple GoldenDict commands found: {', '.join(executables)}")
41 | warning(f"Using {self.executable}")
42 |
43 | def defaultTrigger(self):
44 | return "gd "
45 |
46 | def handleTriggerQuery(self, query):
47 | q = query.string.strip()
48 | query.add(
49 | StandardItem(
50 | id=md_name,
51 | text=md_name,
52 | subtext=f"Look up '{q}' in GoldenDict",
53 | iconUrls=self.iconUrls,
54 | actions=[Action(md_name, md_name, lambda e=self.executable: runDetachedProcess([e, q]))],
55 | )
56 | )
57 |
--------------------------------------------------------------------------------
/aur/arch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/arch_wiki/arch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pacman/arch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tex_to_unicode/tex.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/color/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | """
5 | Displays a color parsed from a code, which may be in one of these formats:
6 |
7 | * #RGB (each of R, G, and B is a single hex digit)
8 | * #RRGGBB
9 | * #AARRGGBB
10 | * #RRRGGGBBB
11 | * #RRRRGGGGBBBB
12 |
13 | Note: This extension started as a prototype to test the internal color pixmap generator. However it may serve as a \
14 | starting point for people having a real need for color workflows. PR's welcome.
15 | """
16 |
17 | from albert import *
18 | from string import hexdigits
19 |
20 | md_iid = "3.0"
21 | md_version = "2.0"
22 | md_name = "Color"
23 | md_description = "Display color for color codes"
24 | md_license = "MIT"
25 | md_url = "https://github.com/albertlauncher/python/tree/main/color"
26 | md_authors = "@manuelschneid3r"
27 |
28 |
29 | class Plugin(PluginInstance, GlobalQueryHandler):
30 |
31 | def __init__(self):
32 | PluginInstance.__init__(self)
33 | GlobalQueryHandler.__init__(self)
34 |
35 | def defaultTrigger(self):
36 | return '#'
37 |
38 | def handleGlobalQuery(self, query):
39 | rank_items = []
40 | s = query.string.strip()
41 | if s:
42 | if s.startswith('#'): # remove hash
43 | s = s[1:]
44 |
45 | # check length and hex
46 | if any([len(s) == l for l in [3, 6, 8, 9, 12]]) and all(c in hexdigits for c in s):
47 | rank_items.append(
48 | RankItem(
49 | StandardItem(
50 | id=self.id(),
51 | text=s,
52 | subtext="The color for this code.",
53 | iconUrls=[f"gen:?background=%23{s}"],
54 | ),
55 | 1
56 | )
57 | )
58 |
59 | return rank_items
60 |
61 | def configWidget(self):
62 | return [{ 'type': 'label', 'text': __doc__.strip() }]
63 |
--------------------------------------------------------------------------------
/.archive/dango_kao/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Find kaomojis using getdango.com
4 |
5 | Dango uses a form of artificial intelligence called deep learning to understand the nuances of \
6 | human emotion, and predict emoji based on what you type.
7 |
8 | Synopsis: """
9 |
10 | from albert import *
11 | import os
12 | import json
13 | import urllib.error
14 | from urllib.request import urlopen, Request
15 | from urllib.parse import urlencode
16 |
17 | __title__ = "Dango Kaomoji"
18 | __version__ = "0.4.0"
19 | __triggers__ = "kao "
20 | __authors__ = "David Britt"
21 |
22 | icon_path = os.path.dirname(__file__) + "/kaoicon.svg"
23 | dangoUrl = "https://customer.getdango.com/dango/api/query/kaomoji"
24 |
25 |
26 | def handleQuery(query):
27 | results = []
28 |
29 | if query.isTriggered:
30 |
31 | item = Item(
32 | id=__title__,
33 | icon=icon_path,
34 | text=__title__,
35 | )
36 |
37 | if len(query.string) >= 2:
38 | try:
39 | url = "%s?%s" % (dangoUrl, urlencode({"q": query.string}))
40 | with urlopen(Request(url)) as response:
41 | json_data = json.loads(response.read().decode())
42 | for emoj in json_data["items"]:
43 | results.append(Item(
44 | id=__title__,
45 | icon=icon_path,
46 | text=emoj["text"],
47 | actions=[
48 | ClipAction(
49 | "Copy translation to clipboard", emoj["text"])
50 | ]
51 | ))
52 | except urllib.error.URLError as urlerr:
53 | print("Troubleshoot internet connection: %s" % urlerr)
54 | item.subtext = "Connection error"
55 | return item
56 | else:
57 | item.subtext = "Search emojis!"
58 | return item
59 |
60 | return results
61 |
--------------------------------------------------------------------------------
/.archive/xkcd/README.md:
--------------------------------------------------------------------------------
1 | # Albert xkcd Plugin
2 |
3 |
4 |
5 |
6 |
7 |
8 | ## Description
9 |
10 | The xkcd Albert plugin lets you launch an xkcd comic in your browser from the
11 | albert prompt. Here are its main features:
12 |
13 | * On toggle (default trigger: `xkcd`) it shows you the comics in newest-first
14 | order.
15 | * If you want to find a comic with a specific title then you can use fuzzy search to do so):
16 | * `xkcd some words from the title`
17 |
18 | ## Demo
19 |
20 | 
21 |
22 | ## Motivation
23 |
24 | I love reading xkcd. I also wanted to get into developing plugins for Albert
25 | thus this was the perfect opportunity to do so.
26 |
27 | ## Manual installation instructions
28 |
29 | Requirements:
30 |
31 | - Albert - [Installation instructions](https://albertlauncher.github.io/docs/installing/)
32 | - Albert Python Interface: v0.2
33 | - Python version >= 3.5
34 |
35 |
36 | Download and run the ``install-plugin.sh`` script or run the following to do
37 | that automatically:
38 |
39 | ``````sh
40 | curl https://raw.githubusercontent.com/bergercookie/xkcd-albert-plugin/master/install-plugin.sh | bash
41 | ``````
42 |
43 | ## Self Promotion
44 |
45 | If you find this tool useful, please [star it on
46 | Github](https://github.com/bergercookie/xkcd-albert-plugin)
47 |
48 | ## TODO List
49 |
50 | See [ISSUES list](https://github.com/bergercookie/xkcd-albert-plugin/issues) for
51 | the things that I'm currently either working on or interested in implementing in
52 | the near future. In case there's something you are interesting in working on,
53 | don't hesitate to either ask for clarifications or just do it and directly make
54 | a PR.
55 |
--------------------------------------------------------------------------------
/unit_converter/icons/lengthtime.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/phpstorm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/idea.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/rand/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Draws a random integer.
4 |
5 | This extension provides the rand item, which can be used with :
6 | - 1 argument a : draws an integer between 1 and a (included)
7 | - 2 argumens a, b: draws an integer between a and b (included)
8 | - 3 arguments a, b, nb: draws nb integers between a and b (included)
9 |
10 | Synopsis:
11 | rand [min] max [numbers]
12 | """
13 |
14 | import os
15 |
16 | import random
17 |
18 | from albertv0 import *
19 |
20 | __iid__ = "PythonInterface/v0.1"
21 | __prettyname__ = "Rand"
22 | __version__ = "0.1"
23 | __trigger__ = "rand "
24 | __author__ = "Cyprien Ruffino"
25 | __dependencies__ = []
26 |
27 |
28 | usage_string = "Usage: [min] max [numbers]"
29 |
30 |
31 | def createBlankItem(text):
32 | return Item(
33 | id=__prettyname__,
34 | icon="rand/rand.png",
35 | text=str(text),
36 | subtext="",
37 | actions=[])
38 |
39 |
40 | def handleQuery(query):
41 | if query.isTriggered:
42 |
43 | tokens = query.string.split(" ")
44 |
45 | # No arguments
46 | if len(tokens) == 1 and tokens[0] == "":
47 | return createBlankItem(usage_string)
48 |
49 | # At least one argument
50 | try:
51 | tokens = [int(token) for token in tokens]
52 | except ValueError:
53 | return createBlankItem(usage_string)
54 |
55 | if len(tokens) == 1:
56 | b = tokens[0]
57 | rand = random.randint(1, b)
58 | return createBlankItem(str(rand))
59 |
60 | elif len(tokens) == 2:
61 | a, b = tokens
62 | rand = random.randint(a, b)
63 | return createBlankItem(str(rand))
64 |
65 | elif len(tokens) == 3:
66 | a, b, nb = tokens
67 | items = []
68 | for i in range(nb):
69 | rand = random.randint(a, b)
70 | items.append(createBlankItem(rand))
71 | return items
72 |
73 | else:
74 | return createBlankItem(usage_string)
75 |
--------------------------------------------------------------------------------
/.archive/xkcd/install-plugin.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -Eeuo pipefail
3 | ## do this if there's any error with the installation and you want to report a bug
4 | # set -x
5 |
6 | # supplementary funs -----------------------------------------------------------
7 | function announce
8 | {
9 | echo
10 | echo "**********************************************************************"
11 | echo -e "$@"
12 | echo "**********************************************************************"
13 | echo
14 | }
15 |
16 | function announce_err
17 | {
18 | announce "[ERROR] $*"
19 | }
20 |
21 | function install_pkg
22 | {
23 | announce "Installing \"$*\""
24 | pip3 install --user --upgrade "$*"
25 | announce "Installed $*"
26 | }
27 |
28 | function is_installed
29 | {
30 | if [ "$(which "$*" 2>&1 1>/dev/null)" = "1" ]
31 | then
32 | return 1
33 | else
34 | return 0
35 | fi
36 | }
37 |
38 | # Check prereqs ----------------------------------------------------------------
39 | ret=$(is_installed albert)
40 | if [ "$ret" = "1" ]
41 | then
42 | announce_err "Please install albert first. Exiting"
43 | return 1
44 | fi
45 | ret=$(is_installed git)
46 | if [ "$ret" = "1" ]
47 | then
48 | announce_err "Please install git first. Exiting"
49 | return 1
50 | fi
51 |
52 | DST="$HOME/.local/share/albert/org.albert.extension.python/modules"
53 | if [[ ! -d "$DST" ]]
54 | then
55 | announce_err "Local extensions directory doesn't exist. Please check your albert installation. Exiting"
56 | return 1
57 | fi
58 |
59 | # Install ----------------------------------------------------------------------
60 | install_pkg git+https://github.com/tasdikrahman/xkcd-dl
61 | install_pkg fuzzywuzzy
62 |
63 | # Seesm like the xkcd-dl beautifulsoup4 version is outdated
64 | install_pkg beautifulsoup4
65 |
66 | PLUGIN_DIR="$DST/xkcd"
67 | if [ -d "$PLUGIN_DIR" ]
68 | then
69 | rm -rf "$PLUGIN_DIR"
70 | fi
71 | announce "Cloning and installing xkcd-albert-plugin -> $PLUGIN_DIR"
72 | git clone https://github.com/bergercookie/xkcd-albert-plugin "$PLUGIN_DIR"
73 | announce "Installed xkcd-albert-plugin -> $PLUGIN_DIR"
74 |
75 | announce "Plugin ready - Enable it from the Albert settings"
76 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/datagrip.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/texdoc/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """texdoc extension
4 |
5 | This is an extension to search for LaTeX documentation.
6 |
7 | Synopsis: """
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | import re
12 | import subprocess
13 | from pathlib import Path
14 | from albert import *
15 |
16 | __title__ = 'TeXdoc'
17 | __version__ = '0.4.0'
18 | __triggers__ = 'td'
19 | __authors__ = 'Florian Adamsky (@cit)'
20 | __exec_deps__ = ['texdoc']
21 |
22 | iconPath = Path(__file__).parent / 'texdoc-logo.svg'
23 | texdoc_cmd = ['texdoc', '-I', '-q', '-s', '-M']
24 |
25 | def handleQuery(query):
26 | if not query.isTriggered:
27 | return
28 |
29 | query.disableSort()
30 |
31 | stripped_query = query.string.strip()
32 |
33 | if stripped_query:
34 | process = subprocess.run(texdoc_cmd + [stripped_query],
35 | stdout=subprocess.PIPE)
36 | texdoc_output = process.stdout.decode('utf-8')
37 |
38 | results = []
39 | for line in texdoc_output.split("\n"):
40 |
41 | match = re.search('\t(/.*/)([\w\.-]+)\t\t', line, re.IGNORECASE)
42 | if match:
43 | directory = match.group(1).strip()
44 | filename = match.group(2).strip()
45 | full_path = directory.join(['/', filename])
46 |
47 | results.append(Item(id = __title__,
48 | icon = str(iconPath),
49 | text = filename,
50 | subtext = directory,
51 | completion = full_path,
52 | actions = [
53 | ProcAction(text = 'This action opens the documentation.',
54 | commandline=['xdg-open', full_path])
55 | ]))
56 |
57 | return results
58 | else:
59 | return Item(id = __title__,
60 | icon = str(iconPath),
61 | text = __title__,
62 | subtext = 'Enter a query to search with texdoc',
63 | completion = query.rawString)
64 |
--------------------------------------------------------------------------------
/.archive/vpn/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2020 janeklb
3 | # Copyright (c) 2023 Bierchermuesli
4 | # Copyright (c) 2020-2024 Manuel Schneider
5 |
6 | import subprocess
7 | from collections import namedtuple
8 |
9 | from albert import *
10 |
11 | md_iid = "3.0"
12 | md_version = "2.0"
13 | md_name = "VPN"
14 | md_description = "Manage NetworkManager VPN connections"
15 | md_license = "MIT"
16 | md_url = "https://github.com/albertlauncher/python/tree/main/vpn"
17 | md_authors = ["@janeklb", "@Bierchermuesli", "@manuelschneid3r"]
18 | md_bin_dependencies = ["nmcli"]
19 |
20 |
21 | class Plugin(PluginInstance, TriggerQueryHandler):
22 |
23 | VPNConnection = namedtuple('VPNConnection', ['name', 'connected'])
24 |
25 | def __init__(self):
26 | PluginInstance.__init__(self)
27 | TriggerQueryHandler.__init__(self)
28 |
29 | def defaultTrigger(self):
30 | return "vpn "
31 |
32 | def getVPNConnections(self):
33 | consStr = subprocess.check_output(
34 | 'nmcli -t connection show',
35 | shell=True,
36 | encoding='UTF-8'
37 | )
38 | for conStr in consStr.splitlines():
39 | con = conStr.split(':')
40 | if con[2] in ['vpn', 'wireguard']:
41 | yield self.VPNConnection(name=con[0], connected=con[3] != '')
42 |
43 | @staticmethod
44 | def buildItem(con):
45 | name = con.name
46 | command = 'down' if con.connected else 'up'
47 | text = f'Connect to {name}' if command == 'up' else f'Disconnect from {name}'
48 | commandline = ['nmcli', 'connection', command, 'id', name]
49 | return StandardItem(
50 | id=f'vpn-{command}-{name}',
51 | text=name,
52 | subtext=text,
53 | iconUrls=['xdg:network-wired'],
54 | inputActionText=name,
55 | actions=[Action("run", text=text, callable=lambda: runDetachedProcess(commandline))]
56 | )
57 |
58 | def handleTriggerQuery(self, query):
59 | if query.isValid:
60 | connections = self.getVPNConnections()
61 | if query.string:
62 | connections = [con for con in connections if query.string.lower() in con.name.lower()]
63 | query.add([self.buildItem(con) for con in connections])
64 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/dataspell.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/find/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | broken
5 | """
6 | # Copyright (c) 2022 Manuel Schneider
7 |
8 | import albert
9 | from albert import *
10 | from time import sleep
11 | import io
12 | import os
13 | import subprocess
14 | from pathlib import Path
15 |
16 | __iid__ = "0.5"
17 | __version__ = "1.0"
18 | __id__ = "find"
19 | __name__ = "Find"
20 | __description__ = "Online search your file system"
21 | __license__ = "BSD-3"
22 | __url__ = "https://github.com/albertlauncher/python/tree/master/find"
23 | __maintainers__ = "@manuelschneid3r"
24 | __authors__ = ["@manuelschneid3r"]
25 | __bin_dependencies__ = ["find"]
26 | __default_trigger__ = "find "
27 | __synopsis__ = ""
28 |
29 |
30 | class Plugin(Plugin, QueryHandler):
31 | def __init__(self):
32 | albert.Plugin.__init__(self)
33 | albert.QueryHandler.__init__(self)
34 |
35 | def id(self):
36 | return __id__;
37 |
38 | def name(self):
39 | return __name__;
40 |
41 | def takeThisAndModifyR(self, item):
42 | item.id = "takeThisAndModifyR";
43 |
44 | def takeThisAndModifyR_(self, item):
45 | item.id = "takeThisAndModifyR_";
46 |
47 | def takeThisAndModifyP(self, item):
48 | item.id = "takeThisAndModifyP";
49 |
50 | def description(self):
51 | return __description__;
52 |
53 | def handleQuery(self, query):
54 | info(query.string)
55 | proc = subprocess.Popen(["find", Path.home(), "iname", query.string], stdout=subprocess.PIPE)
56 | for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
57 | absolute = os.path.abspath(line)
58 | item = Item(
59 | id=absolute,
60 | text=os.path.basename(absolute),
61 | subtext=absolute,
62 | completion="",
63 | icon=[":python"],
64 | actions=[
65 | Action(
66 | id="clip",
67 | text="setClipboardText (ClipAction)",
68 | callable=lambda: setClipboardText(text=configLocation())
69 | )
70 | ]
71 | )
72 | query.add(item)
73 |
74 |
75 | def initialize(self):
76 | info("Find::initialize")
77 |
78 | # def extensions(self):
79 | # return [self.e]
80 | ## pass
81 |
--------------------------------------------------------------------------------
/.archive/window_switcher/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """List and manage X11 windows.
4 |
5 | Synopsis: """
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import subprocess
10 | from collections import namedtuple
11 | from albert import Item, ProcAction, iconLookup
12 |
13 | __title__ = "Window Switcher"
14 | __version__ = "0.4.5"
15 | __authors__ = ["Ed Perez", "Manuel S.", "dshoreman"]
16 | __exec_deps__ = ["wmctrl"]
17 |
18 | Window = namedtuple("Window", ["wid", "desktop", "wm_class", "host", "wm_name"])
19 |
20 | def handleQuery(query):
21 | stripped = query.string.strip().lower()
22 | if stripped:
23 | results = []
24 | for line in subprocess.check_output(['wmctrl', '-l', '-x']).splitlines():
25 | win = Window(*parseWindow(line))
26 |
27 | if win.desktop == "-1":
28 | continue
29 |
30 | win_instance, win_class = win.wm_class.replace(' ', '-').split('.')
31 | matches = [
32 | win_instance.lower(),
33 | win_class.lower(),
34 | win.wm_name.lower()
35 | ]
36 |
37 | if any(stripped in match for match in matches):
38 | iconPath = iconLookup(win_instance) or iconLookup(win_class.lower())
39 | results.append(Item(id="%s%s" % (__title__, win.wm_class),
40 | icon=iconPath,
41 | text="%s - Desktop %s" % (win_class.replace('-',' '), win.desktop),
42 | subtext=win.wm_name,
43 | actions=[ProcAction("Switch Window",
44 | ["wmctrl", '-i', '-a', win.wid] ),
45 | ProcAction("Move window to this desktop",
46 | ["wmctrl", '-i', '-R', win.wid] ),
47 | ProcAction("Close the window gracefully.",
48 | ["wmctrl", '-c', win.wid])]))
49 | return results
50 |
51 | def parseWindow(line):
52 | win_id, desktop, rest = line.decode().split(None, 2)
53 | win_class, rest = rest.split(' ', 1)
54 | host, title = rest.strip().split(None, 1)
55 |
56 | return [win_id, desktop, win_class, host, title]
57 |
--------------------------------------------------------------------------------
/.archive/dango_kao/kaoicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/mathematica_eval/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import subprocess
4 | from tempfile import NamedTemporaryFile
5 | from threading import Lock
6 |
7 | from albert import *
8 |
9 | md_iid = "2.0"
10 | md_version = "1.1"
11 | md_name = "Mathematica Eval"
12 | md_description = "Evaluate Mathemtica code"
13 | md_license = "GPL-3.0"
14 | md_url = "https://github.com/albertlauncher/python/tree/master/mathematica_eval"
15 | md_maintainers = "@tyilo"
16 | md_bin_dependencies = ["wolframscript"]
17 |
18 |
19 | class Plugin(PluginInstance, TriggerQueryHandler):
20 |
21 | def __init__(self):
22 | TriggerQueryHandler.__init__(self,
23 | id=md_id,
24 | name=md_name,
25 | description=md_description,
26 | synopsis='',
27 | defaultTrigger='mma ')
28 | PluginInstance.__init__(self, extensions=[self])
29 |
30 | def handleTriggerQuery(self, query: TriggerQuery) -> None:
31 | stripped = query.string.strip()
32 | if not stripped:
33 | return
34 |
35 | with NamedTemporaryFile("w") as f:
36 | f.write(stripped)
37 | f.flush()
38 | process = subprocess.Popen(
39 | ["wolframscript", "-print", "-f", f.name],
40 | encoding="utf-8",
41 | stdout=subprocess.PIPE,
42 | )
43 |
44 | while True:
45 | if not query.isValid:
46 | process.kill()
47 | return
48 |
49 | try:
50 | output, _ = process.communicate(timeout=0.1)
51 | break
52 | except subprocess.TimeoutExpired:
53 | pass
54 |
55 | result_str = output.strip()
56 |
57 | query.add(
58 | StandardItem(
59 | id=md_id,
60 | text=result_str,
61 | inputActionText=query.trigger + result_str,
62 | iconUrls=["xdg:wolfram-mathematica"],
63 | actions=[
64 | Action(
65 | "copy",
66 | "Copy result to clipboard",
67 | lambda r=result_str: setClipboardText(r),
68 | ),
69 | ],
70 | )
71 | )
72 |
--------------------------------------------------------------------------------
/.archive/base_converter/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Convert representations of numbers.
4 |
5 | Synopsis:
6 |
7 | [padding]
8 |
9 | where is a literal of the form '0bXXX' (binary), '0XXX' (octal),
10 | or '0xXXX' (hexadecimal)."""
11 |
12 | # Copyright (c) 2022 Manuel Schneider
13 |
14 | import numpy as np
15 | from collections import defaultdict
16 |
17 | from albert import Item, ClipAction
18 |
19 | __title__ = "Base Converter"
20 | __version__ = "0.4.1"
21 | __triggers__ = "base "
22 | __authors__ = ["Manuel S.", "Keating950"]
23 | __py_deps__ = ["numpy"]
24 |
25 |
26 | class keyed_defaultdict(defaultdict):
27 | def __missing__(self, key):
28 | return self.default_factory(key)
29 |
30 |
31 | base_prefixes = keyed_defaultdict(lambda k: 8 if k[0] == "0" and len(k) > 1 else 10)
32 | base_prefixes["0b"] = 2
33 | base_prefixes["0x"] = 16
34 |
35 |
36 | def buildItem(completion, dst, number, padding=0):
37 | item = Item(id=__title__, completion=completion)
38 | try:
39 | src = base_prefixes[number[:2]]
40 | dst = int(dst)
41 | padding = int(padding)
42 | integer = int(number, src)
43 | item.text = np.base_repr(integer, dst)
44 | if integer >= 0 and len(item.text) < padding:
45 | item.text = '0'*(padding-len(item.text)) + item.text
46 | item.subtext = "Base %s representation of %s (base %s)" % (dst, number, src)
47 | item.addAction(ClipAction("Copy to clipboard", item.text))
48 | except Exception as e:
49 | item.text = e.__class__.__name__
50 | item.subtext = str(e)
51 | return item
52 |
53 |
54 | def handleQuery(query):
55 | if query.isTriggered:
56 | fields = query.string.split()
57 | if len(fields) == 2:
58 | return buildItem(query.rawString, fields[0], fields[1])
59 | else:
60 | item = Item(id=__title__)
61 | item.text = __title__
62 | item.subtext = "Enter a query in the form of \"<dstbase> <number>\""
63 | return item
64 | else:
65 | fields = query.string.split()
66 | if len(fields) < 2:
67 | return
68 | src = base_prefixes[fields[:3]]
69 | number = fields[1]
70 | padding = 0 if len(fields) < 3 else fields[2]
71 | results = []
72 | for dst in sorted(base_prefixes.values().append(8)):
73 | if dst == src:
74 | continue
75 | results.append(buildItem(query.rawString, dst, number, padding))
76 | return results
77 |
--------------------------------------------------------------------------------
/tex_to_unicode/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2022 Jonah Lawrence
3 | # Copyright (c) 2024 Manuel Schneider
4 |
5 | import re
6 | import unicodedata
7 | from pathlib import Path
8 | from pylatexenc.latex2text import LatexNodes2Text
9 |
10 | from albert import *
11 |
12 | md_iid = "3.0"
13 | md_version = "2.0"
14 | md_name = "TeX to Unicode"
15 | md_description = "Convert TeX mathmode commands to unicode characters"
16 | md_license = "MIT"
17 | md_url = "https://github.com/albertlauncher/python/tree/main/tex_to_unicode"
18 | md_authors = ["@DenverCoder1", "@manuelschneid3r"]
19 | md_lib_dependencies = "pylatexenc"
20 |
21 |
22 | class Plugin(PluginInstance, TriggerQueryHandler):
23 |
24 | def __init__(self):
25 | PluginInstance.__init__(self)
26 | TriggerQueryHandler.__init__(self)
27 | self.COMBINING_LONG_SOLIDUS_OVERLAY = "\u0338"
28 | self.iconUrls = [f"file:{Path(__file__).parent}/tex.svg"]
29 |
30 | def _create_item(self, text: str, subtext: str, can_copy: bool):
31 | actions = []
32 | if can_copy:
33 | actions.append(
34 | Action(
35 | "copy",
36 | "Copy result to clipboard",
37 | lambda t=text: setClipboardText(t),
38 | )
39 | )
40 | return StandardItem(
41 | id=self.id(),
42 | text=text,
43 | subtext=subtext,
44 | iconUrls=self.iconUrls,
45 | actions=actions,
46 | )
47 |
48 | def defaultTrigger(self):
49 | return "tex "
50 |
51 | def handleTriggerQuery(self, query):
52 | stripped = query.string.strip()
53 |
54 | if not stripped:
55 | return
56 |
57 | if not stripped.startswith("\\"):
58 | stripped = "\\" + stripped
59 |
60 | # Remove double backslashes (newlines)
61 | stripped = stripped.replace("\\\\", " ")
62 |
63 | # pylatexenc doesn't support \not
64 | stripped = stripped.replace("\\not", "@NOT@")
65 |
66 | n = LatexNodes2Text()
67 | result = n.latex_to_text(stripped)
68 |
69 | if not result:
70 | query.add(self._create_item(stripped, "Type some TeX math", False))
71 | return
72 |
73 | # success
74 | result = unicodedata.normalize("NFC", result)
75 | result = re.sub(r"@NOT@\s*(\S)", "\\1" + self.COMBINING_LONG_SOLIDUS_OVERLAY, result)
76 | result = result.replace("@NOT@", "")
77 | result = unicodedata.normalize("NFC", result)
78 | query.add(self._create_item(result, "Result", True))
79 |
--------------------------------------------------------------------------------
/.archive/googletrans/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Translator using py-googletrans
5 | """
6 |
7 | from locale import getdefaultlocale
8 | from pathlib import Path
9 | from time import sleep
10 |
11 | from albert import *
12 | from googletrans import Translator, LANGUAGES
13 |
14 | md_iid = '2.0'
15 | md_version = "1.2"
16 | md_name = "Google Translate"
17 | md_description = "Translate sentences using googletrans"
18 | md_license = "BSD-3"
19 | md_url = "https://github.com/albertlauncher/python/"
20 | md_lib_dependencies = "googletrans==4.0.0-rc1"
21 | md_maintainers = "@manuelschneid3r"
22 |
23 |
24 | class Plugin(PluginInstance, TriggerQueryHandler):
25 |
26 | def __init__(self):
27 | TriggerQueryHandler.__init__(self,
28 | id=md_id,
29 | name=md_name,
30 | description=md_description,
31 | synopsis="[[src] dest] text",
32 | defaultTrigger='tr ')
33 | PluginInstance.__init__(self, extensions=[self])
34 | self.iconUrls = [f"file:{Path(__file__).parent}/google_translate.png"]
35 | self.translator = Translator()
36 | self.lang = getdefaultlocale()[0][0:2]
37 |
38 | def handleTriggerQuery(self, query):
39 | stripped = query.string.strip()
40 | if stripped:
41 | for _ in range(50):
42 | sleep(0.01)
43 | if not query.isValid:
44 | return
45 |
46 | src = None
47 | dest, text = self.lang, stripped
48 | splits = text.split(maxsplit=1)
49 | if 1 < len(splits) and splits[0] in LANGUAGES:
50 | dest, text = splits[0], splits[1]
51 | splits = text.split(maxsplit=1)
52 | if 1 < len(splits) and splits[0] in LANGUAGES:
53 | src = dest
54 | dest, text = splits[0], splits[1]
55 |
56 | if src:
57 | translation = self.translator.translate(text, src=src, dest=dest)
58 | else:
59 | translation = self.translator.translate(text, dest=dest)
60 |
61 | query.add(StandardItem(
62 | id=md_id,
63 | text=translation.text,
64 | subtext=f'From {LANGUAGES[translation.src]} to {LANGUAGES[translation.dest]}',
65 | iconUrls=self.iconUrls,
66 | actions=[Action("copy", "Copy result to clipboard",
67 | lambda t=translation.text: setClipboardText(t))]
68 | ))
69 |
--------------------------------------------------------------------------------
/.archive/npm/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Install, remove and search packages in the npmjs.com database.
4 |
5 | If no search query is supplied you have the option to update all globally installed packages.
6 |
7 | Synopsis: [filter]"""
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | from albert import *
12 | import os
13 | import json
14 | import subprocess
15 |
16 | __title__ = "npm"
17 | __version__ = "0.4.0"
18 | __triggers__ = "npm "
19 | __authors__ = "Benedict Dudel"
20 | __exec_deps__ = ["npm"]
21 |
22 | iconPath = iconLookup("npm") or os.path.dirname(__file__)+"/logo.svg"
23 |
24 | def handleQuery(query):
25 | if query.isTriggered:
26 | if not query.string.strip():
27 | return Item(
28 | id = __title__,
29 | icon = iconPath,
30 | text = "Update",
31 | subtext = "Update all globally installed packages",
32 | actions = [
33 | TermAction("Update packages", ["npm", "update", "--global"])
34 | ]
35 | )
36 |
37 | items = getSearchResults(query.string.strip())
38 | if not items:
39 | return Item(
40 | id = __title__,
41 | icon = iconPath,
42 | text = "Search on npmjs.com",
43 | subtext = "No modules found in local database. Try to search on npmjs.com",
44 | actions = [
45 | UrlAction(
46 | "Search on npmjs.com",
47 | "https://www.npmjs.com/search?q=%s" % query.string.strip()
48 | )
49 | ]
50 | )
51 |
52 | return items
53 |
54 | def getSearchResults(query):
55 | proc = subprocess.run(["npm", "search", "--json", query], stdout=subprocess.PIPE)
56 |
57 | items = []
58 | for module in json.loads(proc.stdout.decode()):
59 | items.append(
60 | Item(
61 | id = __title__,
62 | icon = iconPath,
63 | text = "%s (%s)" % (module["name"], module["version"]),
64 | subtext = module.get("description", ""),
65 | actions = [
66 | UrlAction("Open module on npmjs.com", "https://www.npmjs.com/package/%s" % module["name"]),
67 | TermAction("Install", ["npm", "install", "--global", module["name"]]),
68 | TermAction("Update", ["npm", "update", "--global", module["name"]]),
69 | TermAction("Remove", ["npm", "uninstall", "--global", module["name"]]),
70 | ]
71 | )
72 | )
73 |
74 | return items
75 |
--------------------------------------------------------------------------------
/.archive/inhibit_sleep/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | """
5 | Provides an item 'Inhibit sleep' which can be used to temporarily disable system suspension.
6 |
7 | This is a prototype using `systemd-inhibit`. A sophisticated implementation would probably use the systemd D-Bus \
8 | interface documented [here](https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html).
9 | """
10 |
11 | from albert import *
12 | from subprocess import Popen, TimeoutExpired
13 |
14 | md_iid = '2.3'
15 | md_version = '1.2'
16 | md_name = 'Inhibit sleep'
17 | md_description = 'Inhibit system sleep mode.'
18 | md_license = "MIT"
19 | md_url = 'https://github.com/albertlauncher/python/tree/main/inhibit_sleep'
20 | md_authors = "@manuelschneid3r"
21 | md_bin_dependencies = ['systemd-inhibit', "sleep"]
22 |
23 |
24 | class Plugin(PluginInstance, GlobalQueryHandler):
25 |
26 | def __init__(self):
27 | PluginInstance.__init__(self)
28 | GlobalQueryHandler.__init__(
29 | self, self.id, self.name, self.description,
30 | defaultTrigger='is '
31 | )
32 | self.proc = None
33 |
34 | def finalize(self):
35 | if self.proc:
36 | self.toggle()
37 |
38 | def toggle(self):
39 | if self.proc:
40 | self.proc.terminate()
41 | try:
42 | self.proc.wait(timeout=1)
43 | except TimeoutExpired:
44 | self.proc.kill()
45 | self.proc = None
46 | else:
47 | self.proc = Popen(["systemd-inhibit",
48 | "--what=idle:sleep", "--who=Albert", "--why=User",
49 | "sleep", "infinity"])
50 | info(str(self.proc))
51 |
52 | def configWidget(self):
53 | return [
54 | {
55 | 'type': 'label',
56 | 'text': __doc__.strip(),
57 | 'widget_properties': {
58 | 'textFormat': 'Qt::MarkdownText'
59 | }
60 | }
61 | ]
62 |
63 | def handleGlobalQuery(self, query):
64 | stripped = query.string.strip().lower()
65 | if stripped in "inhibit sleep":
66 | return [
67 | RankItem(
68 | StandardItem(
69 | id=md_name,
70 | text=md_name,
71 | subtext=f"{'Enable' if self.proc else 'Disable'} sleep mode",
72 | iconUrls=[f"gen:?text=💤"],
73 | actions=[Action("inhibit", "Toggle", self.toggle)]
74 | ),
75 | len(stripped)/len(md_name))
76 |
77 | ]
78 | return []
79 |
--------------------------------------------------------------------------------
/locate/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2022-2024 Manuel Schneider
3 |
4 | """
5 | `locate` wrapper. Note that it is up to you to ensure that the locate database is \
6 | up to date. Pass params as necessary. The input is split using a shell lexer.
7 | """
8 |
9 |
10 | import shlex
11 | import subprocess
12 | from pathlib import Path
13 |
14 | from albert import *
15 |
16 | md_iid = "3.0"
17 | md_version = "2.0"
18 | md_name = "Locate"
19 | md_description = "Find and open files using locate"
20 | md_license = "MIT"
21 | md_url = "https://github.com/albertlauncher/python/tree/main/locate"
22 | md_bin_dependencies = "locate"
23 | md_authors = "@manuelschneid3r"
24 |
25 |
26 | class Plugin(PluginInstance, TriggerQueryHandler):
27 |
28 | def __init__(self):
29 | PluginInstance.__init__(self)
30 | TriggerQueryHandler.__init__(self)
31 |
32 | self.iconUrls = [
33 | "xdg:preferences-system-search",
34 | "xdg:system-search",
35 | "xdg:search",
36 | "xdg:text-x-generic",
37 | f"file:{Path(__file__).parent}/locate.svg"
38 | ]
39 |
40 | def synopsis(self, query):
41 | return ""
42 |
43 | def defaultTrigger(self):
44 | return "'"
45 |
46 | def handleTriggerQuery(self, query):
47 | if len(query.string) > 2:
48 |
49 | try:
50 | args = shlex.split(query.string)
51 | except ValueError:
52 | return
53 |
54 | result = subprocess.run(['locate', *args], stdout=subprocess.PIPE, text=True)
55 | if not query.isValid:
56 | return
57 | lines = sorted(result.stdout.splitlines(), reverse=True)
58 | if not query.isValid:
59 | return
60 |
61 | for path in lines:
62 | query.add(
63 | StandardItem(
64 | id=path,
65 | text=Path(path).name,
66 | subtext=path,
67 | iconUrls=self.iconUrls,
68 | actions=[
69 | Action("open", "Open", lambda p=path: openUrl("file://%s" % p))
70 | ]
71 | )
72 | )
73 | else:
74 | query.add(
75 | StandardItem(
76 | id="updatedb",
77 | text="Update locate database",
78 | subtext="Type at least three chars for a search",
79 | iconUrls=self.iconUrls,
80 | actions=[
81 | Action("update", "Update", lambda: runTerminal("sudo updatedb"))
82 | ]
83 | )
84 | )
85 |
--------------------------------------------------------------------------------
/.archive/units/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Convert units.
4 |
5 | This extension is a wrapper for the (extremely) powerful GNU units tool. Note that spaces are \
6 | interpreted as separators, i.e. dont use spaces between numbers and units.
7 |
8 | Synopsis:
9 | [dst]
10 | to """
11 |
12 | # Copyright (c) 2022 Manuel Schneider
13 |
14 | import re
15 | import subprocess as sp
16 | from albert import *
17 |
18 | __title__ = "GNU Units"
19 | __version__ = "0.4.2"
20 | __triggers__ = "units "
21 | __authors__ = ["Manuel S.", "iyzana"]
22 | __exec_deps__ = ["units"]
23 |
24 | icon = iconLookup('calc') or ":python_module"
25 |
26 | regex = re.compile(r"(\S+)(?:\s+to)\s+(\S+)")
27 | unitListOutput = re.compile(r"(\d+(e[+-]\d{2,})?;)+[\d.]+(e[+-]\d{2,})?")
28 |
29 |
30 | def getUnitsResult(args):
31 | command = ['units', '--terse', '--'] + list(args)
32 | query = "units -t -- %s" % ' '.join(args)
33 | try:
34 | output = sp.check_output(command, stderr=sp.STDOUT).decode().strip()
35 |
36 | # usually we want terse output, but when we get a unit-list output
37 | # it looks like this 1;124;18;11;14.025322 which is not friendly
38 | # so we're falling back to not quite terse output
39 | if unitListOutput.fullmatch(output):
40 | command = ['units', '--strict', '--one-line',
41 | '--quiet', '--'] + list(args)
42 | query = "units -s1q -- %s" % ' '.join(args)
43 | output = sp.check_output(
44 | command, stderr=sp.STDOUT).decode().strip()
45 |
46 | return (output, query, True)
47 | except sp.CalledProcessError as e:
48 | return (e.stdout.decode().strip().splitlines()[0], query, False)
49 |
50 |
51 | def handleQuery(query):
52 | if query.isTriggered:
53 | args = query.string.split()
54 | item = Item(id='python.gnu_units', icon=icon)
55 | if args:
56 | result, command, success = getUnitsResult(args)
57 | item.text = result
58 | item.subtext = "Result of '%s'" % command
59 | item.addAction(ClipAction("Copy to clipboard", item.text))
60 | else:
61 | item.text = "Empty input"
62 | item.subtext = "Enter a query of the form []"
63 | return item
64 | else:
65 | match = regex.fullmatch(query.string.strip())
66 | if match:
67 | args = match.group(1, 2)
68 | result, command, success = getUnitsResult(args)
69 | if not success:
70 | return
71 | item = Item(id='python.gnu_units', icon=icon)
72 | item.text = result
73 | item.subtext = "Result of '%s'" % command
74 | item.addAction(ClipAction("Copy to clipboard", item.text))
75 | return item
76 |
--------------------------------------------------------------------------------
/unit_converter/icons/substance.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/.archive/gnote/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Search, open, create and delete notes.
4 |
5 | Synopsis: [filter]"""
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import re
10 | from datetime import datetime
11 |
12 | from dbus import DBusException, Interface, SessionBus
13 |
14 | from albert import *
15 |
16 | __title__ = "Gnote"
17 | __version__ = "0.4.1"
18 | __triggers__ = "gn "
19 | __authors__ = "Manuel S."
20 | __exec_deps__ = ["gnote"]
21 | __py_deps__ = ["dbus"]
22 |
23 | BUS = "org.gnome.%s" % __title__
24 | OBJ = "/org/gnome/%s/RemoteControl" % __title__
25 | IFACE = 'org.gnome.%s.RemoteControl' % __title__
26 | iconPath = iconLookup("gnote")
27 |
28 |
29 | def handleQuery(query):
30 | results = []
31 | if query.isTriggered:
32 | try:
33 | if not SessionBus().name_has_owner(BUS):
34 | warning("Seems like gnote is not running")
35 | return
36 |
37 | obj = SessionBus().get_object(bus_name=BUS, object_path=OBJ)
38 | iface = Interface(obj, dbus_interface=IFACE)
39 |
40 | if query.string.strip():
41 | for note in iface.SearchNotes(query.string.lower(), False):
42 | results.append(
43 | Item(id="%s%s" % (__title__, note),
44 | icon=iconPath,
45 | text=iface.GetNoteTitle(note),
46 | subtext="%s%s" % ("".join(["#%s " % re.search('.+:.+:(.+)', s).group(1) for s in iface.GetTagsForNote(note)]),
47 | datetime.fromtimestamp(iface.GetNoteChangeDate(note)).strftime("Note from %c")),
48 | actions=[
49 | FuncAction("Open note",
50 | lambda note=note: iface.DisplayNote(note)),
51 | FuncAction("Delete note",
52 | lambda note=note: iface.DeleteNote(note))
53 | ]))
54 | else:
55 | def createAndShowNote():
56 | note = iface.CreateNote()
57 | iface.DisplayNote(note)
58 |
59 | results.append(Item(id="%s-create" % __title__,
60 | icon=iconPath,
61 | text=__title__,
62 | subtext="%s notes" % __title__,
63 | actions=[
64 | FuncAction("Open %s" % __title__,
65 | lambda: iface.DisplaySearch()),
66 | FuncAction("Create a new note", createAndShowNote)
67 | ]))
68 | except DBusException as e:
69 | critical(str(e))
70 | return results
71 |
--------------------------------------------------------------------------------
/.archive/tomboy/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Search, open, create and delete Tomboy notes.
4 |
5 | Synopsis: """
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import re
10 | from datetime import datetime
11 | from dbus import DBusException, Interface, SessionBus
12 | from albert import *
13 |
14 | __title__ = "Tomboy"
15 | __version__ = "0.4.1"
16 | __triggers__ = "tb "
17 | __authors__ = "Manuel S."
18 | __exec_deps__ = ["tomboy"]
19 | __py_deps__ = ["dbus"]
20 |
21 | BUS = "org.gnome.%s" % __title__
22 | OBJ = "/org/gnome/%s/RemoteControl" % __title__
23 | IFACE = 'org.gnome.%s.RemoteControl' % __title__
24 | iconPath = iconLookup("tomboy")
25 |
26 | def handleQuery(query):
27 | results = []
28 | if query.isTriggered:
29 | try:
30 | if not SessionBus().name_has_owner(BUS):
31 | warning("Seems like %s is not running" % __title__)
32 | return
33 |
34 | obj = SessionBus().get_object(bus_name=BUS, object_path=OBJ)
35 | iface = Interface(obj, dbus_interface=IFACE)
36 |
37 | if query.string.strip():
38 | for note in iface.SearchNotes(query.string.lower(), False):
39 | results.append(
40 | Item(id="%s%s" % (__title__, note),
41 | icon=iconPath,
42 | text=iface.GetNoteTitle(note),
43 | subtext="%s%s" % ("".join(["#%s " % re.search('.+:.+:(.+)', s).group(1) for s in iface.GetTagsForNote(note)]),
44 | datetime.fromtimestamp(iface.GetNoteChangeDate(note)).strftime("Note from %c")),
45 | actions=[
46 | FuncAction("Open note",
47 | lambda note=note: iface.DisplayNote(note)),
48 | FuncAction("Delete note",
49 | lambda note=note: iface.DeleteNote(note))
50 | ]))
51 | else:
52 | def createAndShowNote():
53 | note = iface.CreateNote()
54 | iface.DisplayNote(note)
55 |
56 | results.append(Item(id="%s-create" % __title__,
57 | icon=iconPath,
58 | text=__title__,
59 | subtext="%s notes" % __title__,
60 | actions=[
61 | FuncAction("Open %s" % __title__,
62 | lambda: iface.DisplaySearch()),
63 | FuncAction("Create a new note", createAndShowNote)
64 | ]))
65 | except DBusException as e:
66 | critical(str(e))
67 | return results
68 |
--------------------------------------------------------------------------------
/copyq/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2017-2024 Manuel Schneider
3 | # Copyright (c) 2023 Oskar Haarklou Veileborg (@BarrensZeppelin)
4 |
5 | import json
6 | import subprocess
7 |
8 | from albert import *
9 |
10 | md_iid = "3.0"
11 | md_version = "2.0"
12 | md_name = "CopyQ"
13 | md_description = "Access CopyQ clipboard"
14 | md_license = "BSD-2-Clause"
15 | md_url = "https://github.com/albertlauncher/python/tree/main/copyq"
16 | md_authors = ["@ManuelSchneid3r", "@BarrensZeppelin"]
17 | md_bin_dependencies = ["copyq"]
18 |
19 |
20 | copyq_script_getAll = r"""
21 | var result=[];
22 | for ( var i = 0; i < size(); ++i ) {
23 | var obj = {};
24 | obj.row = i;
25 | obj.mimetypes = str(read("?", i)).split("\n");
26 | obj.mimetypes.pop();
27 | obj.text = str(read(i));
28 | result.push(obj);
29 | }
30 | JSON.stringify(result);
31 | """
32 |
33 | copyq_script_getMatches = r"""
34 | var result=[];
35 | var match = "%s";
36 | for ( var i = 0; i < size(); ++i ) {
37 | if (str(read(i)).search(new RegExp(match, "i")) !== -1) {
38 | var obj = {};
39 | obj.row = i;
40 | obj.mimetypes = str(read("?", i)).split("\n");
41 | obj.mimetypes.pop();
42 | obj.text = str(read(i));
43 | result.push(obj);
44 | }
45 | }
46 | JSON.stringify(result);
47 | """
48 |
49 |
50 | class Plugin(PluginInstance, TriggerQueryHandler):
51 |
52 | def __init__(self):
53 | PluginInstance.__init__(self)
54 | TriggerQueryHandler.__init__(self)
55 |
56 | def defaultTrigger(self):
57 | return "cp "
58 |
59 | def handleTriggerQuery(self, query):
60 | items = []
61 | script = copyq_script_getMatches % query.string if query.string else copyq_script_getAll
62 | proc = subprocess.run(["copyq", "-"], input=script.encode(), stdout=subprocess.PIPE)
63 | json_arr = json.loads(proc.stdout.decode())
64 |
65 | for json_obj in json_arr:
66 | row = json_obj["row"]
67 | text = json_obj["text"]
68 | if not text:
69 | text = "No text"
70 | else:
71 | text = " ".join(filter(None, text.replace("\n", " ").split(" ")))
72 |
73 | act = lambda s=script, r=row: (
74 | lambda: runDetachedProcess(["copyq", s % r])
75 | )
76 | items.append(
77 | StandardItem(
78 | id=self.id(),
79 | iconUrls=["xdg:copyq"],
80 | text=text,
81 | subtext="%s: %s" % (row, ", ".join(json_obj["mimetypes"])),
82 | actions=[
83 | Action("paste", "Paste", act("select(%s); sleep(60); paste();")),
84 | Action("copy", "Copy", act("select(%s);")),
85 | Action("remove", "Remove", act("remove(%s);")),
86 | ],
87 | )
88 | )
89 |
90 | query.add(items)
91 |
--------------------------------------------------------------------------------
/.archive/pidgin/__init__.py:
--------------------------------------------------------------------------------
1 | """Open Pidgin chats.
2 |
3 | Matching contacts will be suggested.
4 |
5 | Synopsis: """
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | import dbus
10 |
11 | from albert import *
12 |
13 | __title__ = "Pidgin"
14 | __version__ = "0.4.0"
15 | __authors__ = "Greizgh"
16 | __triggers__ = "pidgin "
17 | __exec_deps__ = ["python"]
18 | __py_deps__ = ["dbus"]
19 |
20 | iconPath = iconLookup("pidgin")
21 | bus = dbus.SessionBus()
22 |
23 |
24 | class ContactHandler:
25 | """Handle pidgin contact list"""
26 |
27 | _purple = None
28 | _contacts = []
29 |
30 | def __init__(self):
31 | self.refresh()
32 |
33 | def refresh(self):
34 | """Refresh both dbus connection and pidgin contact list"""
35 | try:
36 | self._contacts = []
37 | self._purple = bus.get_object(
38 | "im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject"
39 | )
40 | accounts = self._purple.PurpleAccountsGetAllActive()
41 | for account in accounts:
42 | buddies = self._purple.PurpleFindBuddies(account, "")
43 | for buddy in buddies:
44 | # if purple.PurpleBuddyIsOnline(buddy):
45 | name = self._purple.PurpleBuddyGetAlias(buddy)
46 | self._contacts.append((name, account))
47 | except dbus.DBusException:
48 | critical("Could not connect to pidgin service")
49 |
50 | def isReady(self):
51 | """Check that this handler is ready to communicate"""
52 | return self._purple is not None
53 |
54 | def chatWith(self, account, name):
55 | """Open a pidgin chat window"""
56 | self._purple.PurpleConversationNew(1, account, name)
57 |
58 | def getMatch(self, query):
59 | """Get buddies matching query"""
60 | normalized = query.lower()
61 | return [item for item in self._contacts if normalized in item[0].lower()]
62 |
63 |
64 | handler = ContactHandler()
65 |
66 |
67 | def handleQuery(query):
68 | if not handler.isReady():
69 | handler.refresh()
70 |
71 | if query.isTriggered:
72 | target = query.string.strip()
73 |
74 | if target:
75 | items = []
76 | for match in handler.getMatch(target):
77 | items.append(
78 | Item(
79 | id=__title__,
80 | icon=iconPath,
81 | text="Chat with {}".format(match[0]),
82 | subtext="Open a pidgin chat window",
83 | completion=match[0],
84 | actions=[
85 | FuncAction(
86 | "Open chat window",
87 | lambda: handler.chatWith(match[1], match[0]),
88 | )
89 | ],
90 | )
91 | )
92 | return items
93 |
--------------------------------------------------------------------------------
/kill/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2022 Manuel Schneider
3 | # Copyright (c) 2022 Benedict Dudel
4 | # Copyright (c) 2022 Pete Hamlin
5 |
6 |
7 | import os
8 | from signal import SIGKILL, SIGTERM
9 |
10 | from albert import *
11 |
12 | md_iid = "3.0"
13 | md_version = "2.0"
14 | md_name = "Kill Process"
15 | md_description = "Kill processes"
16 | md_license = "MIT"
17 | md_url = "https://github.com/albertlauncher/python/tree/main/kill"
18 | md_authors = ["@Pete-Hamlin", "@BenedictDwudel", "@ManuelSchneid3r"]
19 |
20 |
21 | class Plugin(PluginInstance, TriggerQueryHandler):
22 | def __init__(self):
23 | PluginInstance.__init__(self)
24 | TriggerQueryHandler.__init__(self)
25 |
26 | def defaultTrigger(self):
27 | return "kill "
28 |
29 | def handleTriggerQuery(self, query):
30 | if not query.isValid:
31 | return
32 | results = []
33 | uid = os.getuid()
34 | for dir_entry in os.scandir("/proc"):
35 | try:
36 | if dir_entry.name.isdigit() and dir_entry.stat().st_uid == uid:
37 | proc_command = (
38 | open(os.path.join(dir_entry.path, "comm"), "r").read().strip()
39 | )
40 | if query.string in proc_command:
41 | debug(proc_command)
42 | proc_cmdline = (
43 | open(os.path.join(dir_entry.path, "cmdline"), "r")
44 | .read()
45 | .strip()
46 | .replace("\0", " ")
47 | )
48 | results.append(
49 | StandardItem(
50 | id="kill",
51 | iconUrls=["xdg:process-stop"],
52 | text=proc_command,
53 | subtext=proc_cmdline,
54 | actions=[
55 | Action(
56 | "terminate",
57 | "Terminate process",
58 | lambda pid=int(dir_entry.name): os.kill(
59 | pid, SIGTERM
60 | ),
61 | ),
62 | Action(
63 | "kill",
64 | "Kill process",
65 | lambda pid=int(dir_entry.name): os.kill(
66 | pid, SIGKILL
67 | ),
68 | ),
69 | ],
70 | )
71 | )
72 | except FileNotFoundError: # TOCTOU dirs may disappear
73 | continue
74 | except IOError: # TOCTOU dirs may disappear
75 | continue
76 | query.add(results)
77 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/pycharm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/x_window_switcher/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import subprocess
4 | from collections import namedtuple
5 | from albert import *
6 |
7 | md_iid = "3.0"
8 | md_version = "0.6.0"
9 | md_name = "X Window Switcher"
10 | md_description = "Switch X11 Windows"
11 | md_license = "MIT"
12 | md_url = "https://github.com/albertlauncher/python/tree/main/x_window_switcher"
13 | md_bin_dependencies = "wmctrl"
14 | md_authors = ["Ed Perez", "Manuel S.", "dshoreman", "nopsqi"]
15 |
16 | Window = namedtuple("Window", ["wid", "desktop", "wm_class", "host", "wm_name"])
17 |
18 |
19 | class Plugin(PluginInstance, TriggerQueryHandler):
20 | def __init__(self):
21 | PluginInstance.__init__(self)
22 | TriggerQueryHandler.__init__(self)
23 |
24 | # Check for X session and wmctrl availability
25 | try:
26 | subprocess.check_call(["wmctrl", "-m"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
27 | except FileNotFoundError:
28 | raise Exception("wmctrl not found. Please install wmctrl.")
29 | except subprocess.CalledProcessError:
30 | raise Exception("Unable to communicate with X11 window manager. This plugin requires a running X session.")
31 |
32 | def defaultTrigger(self):
33 | return 'w '
34 |
35 | def handleTriggerQuery(self, query):
36 | try:
37 | for line in subprocess.check_output(['wmctrl', '-l', '-x']).splitlines():
38 | win = Window(*parseWindow(line))
39 |
40 | if win.desktop == "-1":
41 | continue
42 |
43 | win_instance, win_class = win.wm_class.replace(' ', '-').split('.', 1)
44 |
45 | m = Matcher(query.string)
46 | if not query.string or m.match(win_instance + ' ' + win_class + ' ' + win.wm_name):
47 | query.add(StandardItem(
48 | id="%s%s" % (md_name, win.wm_class),
49 | iconUrls=["xdg:%s" % win_instance],
50 | text="%s - Desktop %s" % (win_class.replace('-', ' '), win.desktop),
51 | subtext=win.wm_name,
52 | actions=[Action("switch",
53 | "Switch Window",
54 | lambda w=win: runDetachedProcess(["wmctrl", '-i', '-a', w.wid])),
55 | Action("move",
56 | "Move window to this desktop",
57 | lambda w=win: runDetachedProcess(["wmctrl", '-i', '-R', w.wid])),
58 | Action("close",
59 | "Close the window gracefully.",
60 | lambda w=win: runDetachedProcess(["wmctrl", '-c', w.wid]))]
61 | ))
62 | except subprocess.CalledProcessError as e:
63 | warning(f"Error executing wmctrl: {str(e)}")
64 |
65 |
66 | def parseWindow(line):
67 | win_id, desktop, rest = line.decode().split(None, 2)
68 | win_class, rest = rest.split(' ', 1)
69 | host, title = rest.strip().split(None, 1)
70 |
71 | return [win_id, desktop, win_class, host, title]
72 |
--------------------------------------------------------------------------------
/.archive/currency_converter/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Convert currencies.
4 |
5 | Current backends: ECB, Yahoo.
6 |
7 | Synopsis: [to|as|in] """
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | import re
12 | import time
13 | from urllib.request import urlopen
14 | from xml.etree import ElementTree
15 |
16 | from albert import *
17 |
18 | __title__ = "Currency converter"
19 | __version__ = "0.4.0"
20 | __authors__ = "Manuel S."
21 |
22 | iconPath = iconLookup('accessories-calculator') or ":python_module"
23 |
24 |
25 | class EuropeanCentralBank:
26 |
27 | url = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
28 |
29 | def __init__(self):
30 | self.lastUpdate = 0
31 | self.exchange_rates = dict()
32 | self.name = "European Central Bank"
33 |
34 | def convert(self, amount, src, dst):
35 | if self.lastUpdate < time.time()-10800: # Update every 3 hours
36 | self.exchange_rates.clear()
37 | with urlopen(EuropeanCentralBank.url) as response:
38 | tree = ElementTree.fromstring(response.read().decode())
39 | for child in tree[2][0]:
40 | curr = child.attrib['currency']
41 | rate = float(child.attrib['rate'])
42 | self.exchange_rates[curr] = rate
43 | self.exchange_rates["EUR"] = 1.0 # For simpler algorithmic
44 | info("%s: Updated foreign exchange rates." % __title__)
45 | debug(str(self.exchange_rates))
46 | self.lastUpdate = time.time()
47 |
48 | if src in self.exchange_rates and dst in self.exchange_rates:
49 | src_rate = self.exchange_rates[src]
50 | dst_rate = self.exchange_rates[dst]
51 | return str(amount / src_rate * dst_rate)
52 |
53 | class Yahoo:
54 |
55 | def __init__(self):
56 | self.name = "Yahoo"
57 |
58 | def convert(self, amount, src, dst):
59 | if amount.is_integer:
60 | amount = int(amount)
61 | url = 'https://search.yahoo.com/search?p=%s+%s+to+%s' % (amount, src, dst)
62 | with urlopen(url) as response:
63 | html = response.read().decode()
64 | m = re.search('(\d+(\.\d+)?)', html)
65 | if m:
66 | return m.group(1)
67 |
68 |
69 | providers = [EuropeanCentralBank(), Yahoo()]
70 | regex = re.compile(r"(\d+\.?\d*)\s+(\w{3})(?:\s+(?:to|in|as))?\s+(\w{3})")
71 |
72 | def handleQuery(query):
73 | match = regex.fullmatch(query.string.strip())
74 | if match:
75 | prep = (float(match.group(1)), match.group(2).upper(), match.group(3).upper())
76 | item = Item(id=__title__, icon=iconPath)
77 | for provider in providers:
78 | result = provider.convert(*prep)
79 | if result:
80 | item.text = result
81 | item.subtext = "Value of %s %s in %s (Source: %s)" % (*prep, provider.name)
82 | item.addAction(ClipAction("Copy result to clipboard", result))
83 | return item
84 | else:
85 | warning("None of the foreign exchange rate providers came up with a result for %s" % str(prep))
86 |
--------------------------------------------------------------------------------
/jetbrains_projects/icons/androidstudio.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.archive/dango_emoji/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Find emojis using getdango.com
4 |
5 | Dango uses a form of artificial intelligence called deep learning to understand the nuances of \
6 | human emotion, and predict emoji based on what you type. If your emojis are not rendering properly \
7 | install an emoji font like e.g.: https://github.com/eosrei/emojione-color-font
8 |
9 | Synopsis: """
10 |
11 |
12 | from albert import *
13 | import json
14 | import os
15 | import urllib.error
16 | from urllib.request import urlopen, Request
17 | from urllib.parse import urlencode
18 |
19 |
20 | __title__ = "Dango Emoji"
21 | __version__ = "0.4.1"
22 | __triggers__ = ":"
23 | __authors__ = "David Britt"
24 |
25 |
26 | iconPath = os.path.dirname(__file__) + "/dangoemoji.png"
27 | dangoUrl = "https://emoji.getdango.com/api/emoji"
28 | emojipedia_url = "https://emojipedia.org/%s"
29 |
30 |
31 | def handleQuery(query):
32 | results = []
33 | if query.isTriggered:
34 |
35 | item = Item(
36 | id=__title__,
37 | icon=icon_path,
38 | text=__title__
39 | )
40 |
41 | if len(query.string) >= 2:
42 | try:
43 | url = "%s?%s" % (dangoUrl, urlencode({"q": query.string, "syn": 0}))
44 | with urlopen(Request(url)) as response:
45 |
46 | json_data = json.loads(response.read().decode())
47 |
48 | if json_data["results"][0]["score"] > 0.025:
49 | all_emojis = []
50 | for emoj in json_data["results"]:
51 | if emoj["score"] > 0.025:
52 | all_emojis.append(emoj["text"])
53 |
54 | string_emojis = ''.join(all_emojis)
55 |
56 | results.append(Item(
57 | id=__title__,
58 | icon=icon_path,
59 | text=string_emojis,
60 | subtext="Score > 0.025",
61 | actions=[
62 | ClipAction(
63 | "Copy translation to clipboard", string_emojis)
64 | ]
65 | ))
66 |
67 | for emoj in json_data["results"]:
68 | results.append(Item(
69 | id=__title__,
70 | icon=icon_path,
71 | text=str(emoj["text"]),
72 | subtext=str(emoj["score"]),
73 | actions=[
74 | ClipAction(
75 | "Copy translation to clipboard", str(emoj["text"])),
76 | UrlAction("Open in Emojipedia",
77 | emojipedia_url % str(emoj["text"]))
78 | ]
79 | ))
80 |
81 | except urllib.error.URLError as urlerr:
82 | print("Troubleshoot internet connection: %s" % urlerr)
83 | item.subtext = "Connection error"
84 | return item
85 | else:
86 | item.subtext = "Search emojis!"
87 | return item
88 | return results
89 |
--------------------------------------------------------------------------------
/.archive/bitfinex/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Access the Bitfinex markets.
4 |
5 | Synopsis:
6 | filter
7 | [filter]"""
8 |
9 | from albert import *
10 | import time
11 | import os
12 | import urllib.request
13 | import urllib.error
14 | import json
15 | from collections import namedtuple
16 | from threading import Thread, Event
17 |
18 | __title__ = "BitFinex"
19 | __version__ = "0.4.0"
20 | __triggers__ = "bfx "
21 | __authors__ = "Manuel S."
22 |
23 | iconPath = os.path.dirname(__file__) + "/Bitfinex.svg"
24 | symbolsEndpoint = "https://api.bitfinex.com/v1/symbols"
25 | tradeUrl = "https://www.bitfinex.com/t/%s:%s"
26 | markets = []
27 | thread = None
28 |
29 | Market = namedtuple("Market" , ["base", "quote"])
30 |
31 | class UpdateThread(Thread):
32 | def __init__(self):
33 | super().__init__()
34 | self._stopevent = Event()
35 |
36 | def run(self):
37 | while True:
38 | global thread
39 | try:
40 | global markets
41 | with urllib.request.urlopen(symbolsEndpoint) as response:
42 | symbols = json.loads(response.read().decode())
43 | markets.clear()
44 | for symbol in symbols:
45 | symbol = symbol.upper()
46 | markets.append(Market(base=symbol[0:3], quote=symbol[3:6]))
47 | info("Bitfinex markets updated.")
48 | self._stopevent.wait(3600) # Sleep 1h, wakeup on stop event
49 | except Exception as e:
50 | warning("Updating Bitfinex markets failed: %s" % str(e))
51 | self._stopevent.wait(60) # Sleep 1 min, wakeup on stop event
52 |
53 | if self._stopevent.is_set():
54 | return
55 |
56 | def stop(self):
57 | self._stopevent.set()
58 |
59 |
60 | def initialize():
61 | global thread
62 | thread = UpdateThread()
63 | thread.start()
64 |
65 |
66 | def finalize():
67 | global thread
68 | if thread is not None:
69 | thread.stop()
70 | thread.join()
71 |
72 | def makeItem(market):
73 | url = tradeUrl % (market.base, market.quote)
74 | return Item(
75 | id="%s_%s%s" % (__title__, market.base, market.quote),
76 | icon=iconPath,
77 | text="%s/%s" % (market.base, market.quote),
78 | subtext="Open the %s/%s market on bitfinex.com" % (market.base, market.quote),
79 | completion="%s%s%s" % (__triggers__, market.base, market.quote),
80 | actions=[
81 | UrlAction("Show market in browser", url),
82 | ClipAction('Copy URL to clipboard', url)
83 | ]
84 | )
85 |
86 |
87 | def handleQuery(query):
88 | items = []
89 | stripped = query.string.strip().upper()
90 |
91 | if query.isTriggered:
92 | if stripped:
93 | for market in markets:
94 | if ("%s%s" % (market.base, market.quote)).startswith(stripped):
95 | items.append(makeItem(market))
96 | else:
97 | for market in markets:
98 | items.append(makeItem(market))
99 | else:
100 | for market in markets:
101 | if stripped and ("%s%s" % (market.base, market.quote)).startswith(stripped):
102 | items.append(makeItem(market))
103 |
104 | return items
105 |
--------------------------------------------------------------------------------
/.archive/scrot/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Take screenshots of screens, areas or windows.
4 |
5 | This extension wraps the command line utility scrot to make screenshots from albert. When the \
6 | screenshot was made you will hear a sound which indicates that the screenshot was taken \
7 | successfully.Screenshots will be saved in XDG_PICTURES_DIR or in the temp directory.
8 |
9 | Synopsis: """
10 |
11 | # Copyright (c) 2022 Manuel Schneider
12 |
13 | import os
14 | import subprocess
15 | import tempfile
16 | from shutil import which
17 |
18 | from albert import FuncAction, Item, iconLookup
19 |
20 | __title__ = "SCReenshOT utility"
21 | __version__ = "0.4.0"
22 | __triggers__ = "scrot "
23 | __authors__ = "Benedict Dudel"
24 | __exec_deps__ = ["scrot", "xclip"]
25 |
26 | iconPath = iconLookup("camera-photo")
27 |
28 |
29 | def handleQuery(query):
30 | if query.isTriggered:
31 | return [
32 | Item(
33 | id = "%s-whole-screen" % __title__,
34 | icon = iconPath,
35 | text = "Screen",
36 | subtext = "Take a screenshot of the whole screen",
37 | actions = [
38 | FuncAction(
39 | "Take screenshot of whole screen",
40 | lambda: doScreenshot([])
41 | ),
42 | FuncAction(
43 | "Take screenshot of multiple displays",
44 | lambda: doScreenshot(["--multidisp"])
45 | ),
46 | ]
47 | ),
48 | Item(
49 | id = "%s-area-of-screen" % __title__,
50 | icon = iconPath,
51 | text = "Area",
52 | subtext = "Draw a rectangle with your mouse to capture an area",
53 | actions = [
54 | FuncAction(
55 | "Take screenshot of selected area",
56 | lambda: doScreenshot(["--select"])
57 | ),
58 | ]
59 | ),
60 | Item(
61 | id = "%s-current-window" % __title__,
62 | icon = iconPath,
63 | text = "Window",
64 | subtext = "Take a screenshot of the current active window",
65 | actions = [
66 | FuncAction(
67 | "Take screenshot of window with borders",
68 | lambda: doScreenshot(["--focused", "--border"])
69 | ),
70 | FuncAction(
71 | "Take screenshot of window without borders",
72 | lambda: doScreenshot(["--focused"])
73 | ),
74 | ]
75 | ),
76 | ]
77 |
78 | def getScreenshotDirectory():
79 | if which("xdg-user-dir") is None:
80 | return tempfile.gettempdir()
81 |
82 | proc = subprocess.run(["xdg-user-dir", "PICTURES"], stdout=subprocess.PIPE)
83 |
84 | pictureDirectory = proc.stdout.decode("utf-8")
85 | if pictureDirectory:
86 | return pictureDirectory.strip()
87 |
88 | return tempfile.gettempdir()
89 |
90 | def doScreenshot(additionalArguments):
91 | file = os.path.join(getScreenshotDirectory(), "%Y-%m-%d-%T-screenshot.png")
92 |
93 | command = "sleep 0.1 && scrot --exec 'xclip -selection c -t image/png < $f' %s " % file
94 | proc = subprocess.Popen(command + " ".join(additionalArguments), shell=True)
95 |
--------------------------------------------------------------------------------
/.archive/packagist/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Search for PHP packages on Packagist.
4 |
5 | To install packages you need to have installed composer. By default this extension will search by \
6 | package name. But searching for packages by type or tag is supported as well.
7 |
8 | Synopsis: [tag|type] """
9 |
10 | # Copyright (c) 2022 Manuel Schneider
11 |
12 | from albert import *
13 | import os
14 | import json
15 | import urllib.request
16 |
17 | __title__ = "Packagist"
18 | __version__ = "0.4.0"
19 | __triggers__ = "packagist "
20 | __authors__ = "Benedict Dudel"
21 | __exec_deps__ = ["composer"]
22 |
23 | iconPath = os.path.dirname(__file__)+"/logo.png"
24 |
25 |
26 | def handleQuery(query):
27 | if query.isTriggered:
28 | if not query.string.strip():
29 | return [
30 | Item(
31 | id = "packagist-search-by-tag",
32 | icon = iconPath,
33 | text = "by tag",
34 | subtext = "Searching for packages by tag",
35 | completion = "%stag " % __triggers__,
36 | actions=[]
37 | ),
38 | Item(
39 | id = "packagist-search-by-type",
40 | icon = iconPath,
41 | text = "by type",
42 | subtext = "Searching for packages by type",
43 | completion = "%stype " % __triggers__,
44 | actions=[]
45 | )
46 | ]
47 |
48 | if query.string.strip().startswith("tag "):
49 | if query.string.strip()[4:]:
50 | return getItems("https://packagist.org/search.json?tags=%s" % query.string.strip()[4:])
51 |
52 | if query.string.strip().startswith("type "):
53 | if query.string.strip()[5:]:
54 | return getItems("https://packagist.org/search.json?type=%s" % query.string.strip()[5:])
55 |
56 | return getItems("https://packagist.org/search.json?q=%s" % query.string)
57 |
58 | def getItems(url):
59 | items = []
60 | with urllib.request.urlopen(url) as uri:
61 | packages = json.loads(uri.read().decode())
62 | for package in packages['results']:
63 | items.append(
64 | Item(
65 | id = "packagist-package-%s" % package["name"],
66 | icon = iconPath,
67 | text = package["name"],
68 | subtext = package["description"],
69 | completion = "%sname %s" % (__triggers__, package["name"]),
70 | actions = [
71 | UrlAction(
72 | text = "Open on packagist.org",
73 | url = package["url"]
74 | ),
75 | UrlAction(
76 | text = "Open url of repository",
77 | url = package["repository"]
78 | ),
79 | TermAction(
80 | text = "Install",
81 | commandline = ["composer", "global", "require", package['name']]
82 | ),
83 | TermAction(
84 | text = "Remove",
85 | commandline = ["composer", "global", "remove", package['name']]
86 | )
87 | ]
88 | )
89 | )
90 |
91 | return items
92 |
--------------------------------------------------------------------------------
/arch_wiki/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | import json
5 | from pathlib import Path
6 | from time import sleep
7 | from urllib import request, parse
8 |
9 | from albert import *
10 |
11 | md_iid = "3.0"
12 | md_version = '2.0'
13 | md_name = "Arch Linux Wiki"
14 | md_description = "Search Arch Linux Wiki articles"
15 | md_license = "MIT"
16 | md_url = "https://github.com/albertlauncher/python/tree/main/arch_wiki"
17 | md_authors = "@manuelschneid3r"
18 |
19 |
20 | class Plugin(PluginInstance, TriggerQueryHandler):
21 |
22 | baseurl = 'https://wiki.archlinux.org/api.php'
23 | search_url = "https://wiki.archlinux.org/index.php?search=%s"
24 | user_agent = "org.albert.extension.python.archwiki"
25 | iconUrls = [f"file:{Path(__file__).parent}/arch.svg"]
26 |
27 | def __init__(self):
28 | PluginInstance.__init__(self)
29 | TriggerQueryHandler.__init__(self)
30 |
31 | def defaultTrigger(self):
32 | return 'awiki '
33 |
34 | def handleTriggerQuery(self, query):
35 | stripped = query.string.strip()
36 | if stripped:
37 |
38 | # avoid rate limiting
39 | for _ in range(50):
40 | sleep(0.01)
41 | if not query.isValid:
42 | return
43 |
44 | results = []
45 |
46 | params = {
47 | 'action': 'opensearch',
48 | 'search': stripped,
49 | 'limit': "max",
50 | 'redirects': 'resolve',
51 | 'utf8': 1,
52 | 'format': 'json'
53 | }
54 | get_url = "%s?%s" % (self.baseurl, parse.urlencode(params))
55 | req = request.Request(get_url, headers={'User-Agent': self.user_agent})
56 |
57 | with request.urlopen(req) as response:
58 | data = json.loads(response.read().decode())
59 | for i in range(0, len(data[1])):
60 | title = data[1][i]
61 | summary = data[2][i]
62 | url = data[3][i]
63 |
64 | results.append(StandardItem(id=self.id(),
65 | text=title,
66 | subtext=summary if summary else url,
67 | iconUrls=self.iconUrls,
68 | actions=[
69 | Action("open", "Open article", lambda u=url: openUrl(u)),
70 | Action("copy", "Copy URL", lambda u=url: setClipboardText(u))
71 | ]))
72 | if results:
73 | query.add(results)
74 | else:
75 | query.add(StandardItem(id=self.id(),
76 | text="Search '%s'" % query.string,
77 | subtext="No results. Start online search on Arch Wiki",
78 | iconUrls=self.iconUrls,
79 | actions=[Action("search", "Open search",
80 | lambda s=query.string: openUrl(self.search_url % s))]))
81 |
82 | else:
83 | query.add(StandardItem(id=self.id(),
84 | text=md_name,
85 | iconUrls=self.iconUrls,
86 | subtext="Enter a query to search on the Arch Wiki"))
87 |
--------------------------------------------------------------------------------
/.archive/binance/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Access the Binance markets.
4 |
5 | Synopsis:
6 | filter
7 | [filter]"""
8 |
9 | # Copyright (c) 2022 Manuel Schneider
10 |
11 | from albert import *
12 | import time
13 | import os
14 | import urllib.request
15 | import urllib.error
16 | from collections import namedtuple
17 | import json
18 | from threading import Thread, Event
19 |
20 | __title__ = "Binance"
21 | __version__ = "0.4.3"
22 | __triggers__ = "bnc "
23 | __authors__ = "Manuel S."
24 |
25 | iconPath = os.path.dirname(__file__) + "/Binance.svg"
26 | exchangeInfoUrl = "https://api.binance.com/api/v1/exchangeInfo"
27 | tradeUrl = "https://www.binance.com/en/trade/%s_%s?layout=pro"
28 | markets = []
29 | thread = None
30 |
31 | Market = namedtuple("Market" , ["base", "quote"])
32 |
33 | class UpdateThread(Thread):
34 | def __init__(self):
35 | super().__init__()
36 | self._stopevent = Event()
37 |
38 | def run(self):
39 | while True:
40 | global thread
41 | try:
42 | global markets
43 | with urllib.request.urlopen(exchangeInfoUrl) as response:
44 | symbols = json.loads(response.read().decode())['symbols']
45 | markets.clear()
46 | for symbol in symbols:
47 | # Skip this strange 123456 market
48 | if symbol['baseAsset'] != "123":
49 | markets.append(Market(base=symbol['baseAsset'],
50 | quote=symbol['quoteAsset']))
51 | info("Binance markets updated.")
52 | self._stopevent.wait(3600) # Sleep 1h, wakeup on stop event
53 | except Exception as e:
54 | warning("Updating Binance markets failed: %s" % str(e))
55 | self._stopevent.wait(60) # Sleep 1 min, wakeup on stop event
56 |
57 | if self._stopevent.is_set():
58 | return
59 |
60 | def stop(self):
61 | self._stopevent.set()
62 |
63 |
64 | def initialize():
65 | global thread
66 | thread = UpdateThread()
67 | thread.start()
68 |
69 |
70 | def finalize():
71 | global thread
72 | if thread is not None:
73 | thread.stop()
74 | thread.join()
75 |
76 |
77 | def makeItem(market):
78 | url = tradeUrl % (market.base, market.quote)
79 | return Item(
80 | id="%s_%s%s" % (__title__, market.base, market.quote),
81 | icon=iconPath,
82 | text="%s/%s" % (market.base, market.quote),
83 | subtext="Open the %s/%s market on binance.com" % (market.base, market.quote),
84 | completion="%s%s%s" % (__triggers__, market.base, market.quote),
85 | actions=[
86 | UrlAction("Show market in browser", url),
87 | ClipAction('Copy URL to clipboard', url)
88 | ]
89 | )
90 |
91 |
92 | def handleQuery(query):
93 | items = []
94 | stripped = query.string.strip().upper()
95 |
96 | if query.isTriggered:
97 | if stripped:
98 | for market in markets:
99 | if ("%s%s" % (market.base, market.quote)).startswith(stripped):
100 | items.append(makeItem(market))
101 | else:
102 | for market in markets:
103 | items.append(makeItem(market))
104 | else:
105 | for market in markets:
106 | if stripped and ("%s%s" % (market.base, market.quote)).startswith(stripped):
107 | items.append(makeItem(market))
108 |
109 | return items
110 |
--------------------------------------------------------------------------------
/.archive/lpass/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """LastPass Vault Search
4 |
5 | Synopsis: """
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | from shutil import which
10 | from albert import *
11 | import subprocess
12 | import re
13 | import os
14 |
15 | __title__ = 'LastPass'
16 | __version__ = '0.4.1'
17 | __triggers__ = 'lp '
18 | __authors__ = 'David Piçarra'
19 | __exec_deps__ = ['lpass']
20 |
21 | if not which('lpass'):
22 | raise Exception("`lpass` is not in $PATH.")
23 | clipmgrs = ['xclip', 'xsel', 'pbcopy', 'putclip']
24 | hasclipmgr = False
25 | for mgr in clipmgrs:
26 | if which(mgr):
27 | hasclipmgr = True
28 | break
29 | if not hasclipmgr:
30 | raise Exception("`xclip`, `xsel`, `pbcopy`, or `putclip` is not in $PATH.")
31 |
32 | ICON_PATH = os.path.dirname(__file__)+"/lastpass.svg"
33 |
34 | def handleQuery(query):
35 | if query.isTriggered:
36 | stripped = query.string.strip()
37 |
38 | try:
39 | lpass = subprocess.check_output(['lpass', 'status'])
40 | except Exception as e:
41 | return Item(
42 | id=__title__,
43 | icon=ICON_PATH,
44 | text=f'Not logged in.',
45 | subtext=f'Please enter your lastpass email address',
46 | actions=[
47 | ProcAction("lpass login with given email", ["lpass", "login", stripped]),
48 | ]
49 | )
50 |
51 |
52 | if stripped:
53 | try:
54 | lpass = subprocess.Popen(['lpass', 'ls', '--long'], stdout=subprocess.PIPE)
55 | try:
56 | output = subprocess.check_output(['grep', '-i', stripped], stdin=lpass.stdout)
57 | except subprocess.CalledProcessError as e:
58 | return Item(
59 | id=__title__,
60 | icon=ICON_PATH,
61 | text=__title__,
62 | subtext=f'No results found for {stripped}'
63 | )
64 | items = []
65 | for line in output.splitlines():
66 | match = re.match(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2} (.*) \[id: (\d*)\] \[username: (.*)\]', line.decode("utf-8"))
67 | items.append(Item(
68 | id=__title__,
69 | icon=ICON_PATH,
70 | text=match.group(1),
71 | subtext=match.group(3),
72 | actions=[
73 | ProcAction("Copy password to clipboard", ["lpass", "show", "-cp", match.group(2)]),
74 | ProcAction("Copy username to clipboard", ["lpass", "show", "-cu", match.group(2)]),
75 | ProcAction("Copy notes to clipboard", ["lpass", "show", "-c", "--notes", match.group(2)])
76 | ]
77 | ))
78 |
79 | return items
80 |
81 | except subprocess.CalledProcessError as e:
82 | return Item(
83 | id=__title__,
84 | icon=ICON_PATH,
85 | text=f'Error: {str(e.output)}',
86 | subtext=str(e),
87 | actions=[ClipAction('Copy CalledProcessError to clipboard', str(e))]
88 | )
89 | except Exception as e:
90 | return Item(
91 | id=__title__,
92 | icon=ICON_PATH,
93 | text=f'Generic Exception: {str(e)}',
94 | subtext=str(e),
95 | actions=[ClipAction('Copy Exception to clipboard', str(e))]
96 | )
97 |
98 | else:
99 | return Item(
100 | id=__title__,
101 | icon=ICON_PATH,
102 | text=__title__,
103 | subtext='Search the LastPass vault'
104 | )
105 |
--------------------------------------------------------------------------------
/docker/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | from pathlib import Path
5 |
6 | import docker
7 | from albert import *
8 |
9 | md_iid = "3.0"
10 | md_version = "4.0"
11 | md_name = "Docker"
12 | md_description = "Manage docker images and containers"
13 | md_license = "MIT"
14 | md_url = "https://github.com/albertlauncher/python/tree/main/docker"
15 | md_authors = "@manuelschneid3r"
16 | md_bin_dependencies = "docker"
17 | md_lib_dependencies = "docker"
18 |
19 |
20 | class Plugin(PluginInstance, TriggerQueryHandler):
21 | # Global query handler not applicable, queries take seconds sometimes
22 |
23 | def __init__(self):
24 | PluginInstance.__init__(self)
25 | TriggerQueryHandler.__init__(self)
26 | self.icon_urls_running = [f"file:{Path(__file__).parent}/running.png"]
27 | self.icon_urls_stopped = [f"file:{Path(__file__).parent}/stopped.png"]
28 | self.client = None
29 |
30 | def synopsis(self, query):
31 | return ""
32 |
33 | def defaultTrigger(self):
34 | return "d "
35 |
36 | def handleTriggerQuery(self, query):
37 | items = []
38 |
39 | if not self.client:
40 | try:
41 | self.client = docker.from_env()
42 | except Exception as e:
43 | items.append(StandardItem(
44 | id='except',
45 | text="Failed starting docker client",
46 | subtext=str(e),
47 | iconUrls=self.icon_urls_running,
48 | ))
49 | return items
50 |
51 | try:
52 | for container in self.client.containers.list(all=True):
53 | if query.string in container.name:
54 | # Create dynamic actions
55 | if container.status == 'running':
56 | actions = [Action("stop", "Stop container", lambda c=container: c.stop()),
57 | Action("restart", "Restart container", lambda c=container: c.restart())]
58 | else:
59 | actions = [Action("start", "Start container", lambda c=container: c.start())]
60 | actions.extend([
61 | Action("logs", "Logs",
62 | lambda c=container.id: runTerminal("docker logs -f %s ; exec $SHELL" % c)),
63 | Action("remove", "Remove (forced, with volumes)",
64 | lambda c=container: c.remove(v=True, force=True)),
65 | Action("copy-id", "Copy id to clipboard",
66 | lambda cid=container.id: setClipboardText(cid))
67 | ])
68 |
69 | items.append(StandardItem(
70 | id=container.id,
71 | text="%s (%s)" % (container.name, ", ".join(container.image.tags)),
72 | subtext="Container: %s" % container.id,
73 | iconUrls=self.icon_urls_running if container.status == 'running' else self.icon_urls_stopped,
74 | actions=actions
75 | ))
76 |
77 | for image in reversed(self.client.images.list()):
78 | for tag in sorted(image.tags, key=len): # order by resulting score
79 | if query.string in tag:
80 | items.append(StandardItem(
81 | id=image.short_id,
82 | text=", ".join(image.tags),
83 | subtext="Image: %s" % image.id,
84 | iconUrls=self.icon_urls_stopped,
85 | actions=[
86 | # Action("run", "Run with command: %s" % query.string,
87 | # lambda i=image, s=query.string: client.containers.run(i, s)),
88 | Action("rmi", "Remove image", lambda i=image: i.remove())
89 | ]
90 | ))
91 | except Exception as e:
92 | warning(str(e))
93 | self.client = None
94 |
95 | query.add(items)
96 |
--------------------------------------------------------------------------------
/.archive/xkcd/__init__.py:
--------------------------------------------------------------------------------
1 | """Fetch xkcd comics like a boss."""
2 |
3 | # Copyright (c) 2022 Manuel Schneider
4 |
5 | from datetime import datetime, timedelta
6 | from pathlib import Path
7 | import json
8 | import os
9 | import subprocess
10 | import sys
11 |
12 | import albertv0 as v0
13 | from fuzzywuzzy import process
14 | from shutil import which
15 |
16 | __iid__ = "PythonInterface/v0.2"
17 | __prettyname__ = "xkcd"
18 | __version__ = "0.1"
19 | __trigger__ = "xkcd"
20 | __author__ = "Nikos Koukis"
21 | __dependencies__ = []
22 | __homepage__ = "https://github.com/bergercookie/xkcd-albert-plugin"
23 |
24 |
25 | # TODO pyproject toml file
26 | # TODO xkcd-dl executable?
27 | # TODO Upload to github - change support url on error
28 | # TODO Send to albert plugins
29 |
30 | if not which("xkcd-dl"):
31 | raise RuntimeError("xkcd-dl not in $PATH - Please install it via pip3 first.")
32 |
33 | iconPath = v0.iconLookup("xkcd")
34 | if not iconPath:
35 | iconPath = os.path.join(os.path.dirname(__file__), "image.png")
36 | SETTINGS_PATH = Path(v0.cacheLocation()) / "xkcd"
37 | LAST_UPDATE_PATH = SETTINGS_PATH / "last_update"
38 | XKCD_DICT = Path.home() / ".xkcd_dict.json"
39 |
40 |
41 | def initialize():
42 | # Called when the extension is loaded (ticked in the settings) - blocking
43 |
44 | # create cache location
45 | SETTINGS_PATH.mkdir(parents=False, exist_ok=True)
46 | if not LAST_UPDATE_PATH.is_file():
47 | update_date_file()
48 | update_xkcd_db()
49 |
50 |
51 | def finalize():
52 | pass
53 |
54 |
55 | def handleQuery(query):
56 | results = []
57 |
58 | # check whether I have downlaoded the latest metadata
59 | with open(LAST_UPDATE_PATH, "r") as f:
60 | date_str = float(f.readline().strip())
61 |
62 | last_date = datetime.fromtimestamp(date_str)
63 | if datetime.now() - last_date > timedelta(days=1): # run an update daily
64 | update_date_file()
65 | update_xkcd_db()
66 |
67 | if query.isTriggered:
68 | try:
69 | with open(XKCD_DICT, "r", encoding="utf-8") as f:
70 | d = json.load(f)
71 |
72 | if len(query.string) in [0, 1]: # Display all items
73 | for k, v in d.items():
74 | results.append(get_as_item(k, v))
75 | else: # fuzzy search
76 | desc_to_item = {item[1]["description"]: item for item in d.items()}
77 | matched = process.extract(
78 | query.string.strip(), list(desc_to_item.keys()), limit=20
79 | )
80 | for m in [elem[0] for elem in matched]:
81 | # bypass a unicode issue - use .get
82 | item = desc_to_item.get(m)
83 | if item:
84 | results.append(get_as_item(*item))
85 |
86 | except Exception as e: # user to report error
87 | results.insert(
88 | 0,
89 | v0.Item(
90 | id=__prettyname__,
91 | icon=iconPath,
92 | text="Something went wrong! Press [ENTER] to copy error and report it",
93 | actions=[
94 | v0.ClipAction(
95 | f"Copy error - report it to {__homepage__[8:]}",
96 | f"{sys.exc_info()}",
97 | )
98 | ],
99 | ),
100 | )
101 |
102 | return results
103 |
104 |
105 | def get_as_item(k: str, v: dict):
106 | return v0.Item(
107 | id=__prettyname__,
108 | icon=iconPath,
109 | text=v["description"],
110 | subtext=v["date-published"],
111 | completion="",
112 | actions=[
113 | v0.UrlAction("Open in xkcd.com", f"https://www.xkcd.com/{k}"),
114 | v0.ClipAction("Copy URL", f"https://www.xkcd.com/{k}"),
115 | ],
116 | )
117 |
118 |
119 | def update_date_file():
120 | now = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
121 | with open(LAST_UPDATE_PATH, "w") as f:
122 | f.write(str(now))
123 |
124 |
125 | def update_xkcd_db():
126 | return subprocess.call(["xkcd-dl", "-u"])
127 |
--------------------------------------------------------------------------------
/unit_converter/icons/currency.svg:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/vscode_projects/icon.svg:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/.archive/unicode_emoji/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Offline Unicode emoji picker.
4 |
5 | Synopsis: [filter]"""
6 |
7 | # Copyright (c) 2022 Manuel Schneider
8 |
9 | from albert import *
10 | from collections import namedtuple
11 | from threading import Thread
12 | import datetime
13 | import os
14 | import subprocess
15 | import urllib.request
16 | import shutil
17 |
18 | __title__ = "Unicode Emojis"
19 | __version__ = "0.4.3"
20 | __triggers__ = ":"
21 | __authors__ = ["Tim Zeitz", "Manuel S."]
22 | __exec_deps__ = ["convert"]
23 |
24 | EmojiSpec = namedtuple('EmojiSpec', ['string', 'name', 'modifiers'])
25 | emoji_data_src_url = "https://unicode.org/Public/emoji/latest/emoji-test.txt"
26 | emoji_data_path = os.path.join(dataLocation(), "emoji.txt")
27 | icon_path_template = os.path.join(cacheLocation(), __name__, "%s.png")
28 | emojiSpecs = []
29 | thread = None
30 |
31 |
32 | class WorkerThread(Thread):
33 | def __init__(self):
34 | super().__init__()
35 | self.stop = False
36 |
37 | def run(self):
38 |
39 | # Create cache dir
40 | cache_dir_path = os.path.join(cacheLocation(), __name__)
41 | if not os.path.exists(cache_dir_path):
42 | os.mkdir(cache_dir_path)
43 |
44 | # Build the index and icon cache
45 | # global emojiSpecs
46 | emojiSpecs.clear()
47 | with open(emoji_data_path) as f:
48 | for line in f:
49 | if "; fully-qualified" in line:
50 | emoji, desc = line.split('#', 1)[-1].split(None, 1)
51 | desc = [d.strip().lower() for d in desc.split(':')]
52 | emojiSpecs.append(EmojiSpec(emoji, desc[0], desc[1] if len(desc)==2 else ""))
53 |
54 | icon_path = icon_path_template % emoji
55 | if not os.path.exists(icon_path):
56 | subprocess.call(["convert", "-pointsize", "64", "-background", "transparent", "pango:%s" % emoji, icon_path])
57 |
58 | if self.stop:
59 | return
60 |
61 | def initialize():
62 | src_directory = os.path.dirname(os.path.realpath(__file__))
63 |
64 | # if no emoji data exists copy offline src as fallback
65 | if not os.path.isfile(emoji_data_path):
66 | shutil.copyfile(os.path.join(src_directory, "emoji.txt"), emoji_data_path)
67 |
68 | current_version = get_emoji_data_version(emoji_data_path)
69 |
70 | try:
71 | new_path = os.path.join(dataLocation(), "emoji-new.txt")
72 |
73 | # try to fetch the latest emoji data
74 | with urllib.request.urlopen(emoji_data_src_url) as response, open(new_path, 'wb') as out_file:
75 | # save it
76 | shutil.copyfileobj(response, out_file)
77 |
78 | # update emoji data if the fetched data is newer
79 | if get_emoji_data_version(new_path) > current_version:
80 | shutil.copyfile(new_path, emoji_data_path)
81 |
82 | os.remove(new_path)
83 |
84 | except Exception as e:
85 | warning(e)
86 |
87 | # Build the index and icon cache
88 | global thread
89 | thread = WorkerThread()
90 | thread.start()
91 |
92 | def finalize():
93 | global thread
94 | if thread is not None:
95 | thread.stop = True
96 | thread.join()
97 |
98 | def get_emoji_data_version(path):
99 | with open(emoji_data_path) as f:
100 | for line in f:
101 | if "# Date: " in line:
102 | return datetime.datetime.strptime(line.strip(), "# Date: %Y-%m-%d, %H:%M:%S GMT")
103 |
104 | def handleQuery(query):
105 | if query.isValid and query.isTriggered:
106 | items = []
107 | query_tokens = query.string.lower().split()
108 | # filter emojiSpecs where all query words are in any of the emoji description words
109 | for es in filter(lambda e: all(any(n in s for s in [e.name, e.modifiers]) for n in query_tokens), emojiSpecs):
110 | items.append(Item(id = "%s%s" % (__name__, es.string),
111 | completion = es.name if not es.modifiers else " ".join([es.name, es.modifiers]),
112 | icon = icon_path_template % es.string,
113 | text = es.name.capitalize(),
114 | subtext = es.modifiers.capitalize() if es.modifiers else "(No modifiers)",
115 | actions = [ClipAction("Copy to clipboard", es.string)]))
116 | return items
117 |
--------------------------------------------------------------------------------
/virtualbox/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 | """
4 | This plugin is based on [virtualbox-python](https://pypi.org/project/virtualbox/) and needs the 'vboxapi' module which
5 | is part of the VirtualBox SDK. Some distributions package the SDK, e.g. Arch has
6 | [virtualbox-sdk](https://archlinux.org/packages/extra/x86_64/virtualbox-sdk/).
7 | """
8 |
9 | import virtualbox
10 | from virtualbox.library import LockType, MachineState
11 |
12 | from albert import *
13 |
14 | md_iid = "3.0"
15 | md_version = "2.0"
16 | md_name = "VirtualBox"
17 | md_description = "Manage your VirtualBox machines"
18 | md_license = "MIT"
19 | md_url = "https://github.com/albertlauncher/python/tree/main/virtualbox"
20 | md_authors = "@manuelschneid3r"
21 | md_lib_dependencies = ['virtualbox']
22 |
23 |
24 | def startVm(vm):
25 | try:
26 | with virtualbox.Session() as session:
27 | progress = vm.launch_vm_process(session, 'gui', [])
28 | progress.wait_for_completion()
29 | except Exception as e:
30 | warning(str(e))
31 |
32 |
33 | def acpiPowerVm(vm):
34 | with vm.create_session(LockType.shared) as session:
35 | session.console.power_button()
36 |
37 |
38 | def stopVm(vm):
39 | with vm.create_session(LockType.shared) as session:
40 | session.console.power_down()
41 |
42 |
43 | def saveVm(vm):
44 | with vm.create_session(LockType.shared) as session:
45 | session.machine.save_state()
46 |
47 |
48 | def discardSavedVm(vm):
49 | with vm.create_session(LockType.shared) as session:
50 | session.machine.discard_save_state(True)
51 |
52 |
53 | def resumeVm(vm):
54 | with vm.create_session(LockType.shared) as session:
55 | session.console.resume()
56 |
57 |
58 | def pauseVm(vm):
59 | with vm.create_session(LockType.shared) as session:
60 | session.console.pause()
61 |
62 |
63 | class Plugin(PluginInstance, TriggerQueryHandler):
64 |
65 | def __init__(self):
66 | PluginInstance.__init__(self)
67 | TriggerQueryHandler.__init__(self)
68 | self.iconUrls = ["xdg:virtualbox", ":unknown"]
69 |
70 | def defaultTrigger(self):
71 | return 'vbox '
72 |
73 | def synopsis(self, query):
74 | return ""
75 |
76 | def configWidget(self):
77 | return [
78 | {
79 | 'type': 'label',
80 | 'text': __doc__.strip(),
81 | 'widget_properties': {
82 | 'textFormat': 'Qt::MarkdownText'
83 | }
84 | }
85 | ]
86 |
87 | def handleTriggerQuery(self, query):
88 | items = []
89 | pattern = query.string.strip().lower()
90 | try:
91 | for vm in filter(lambda vm: pattern in vm.name.lower(), virtualbox.VirtualBox().machines):
92 | actions = []
93 | if vm.state == MachineState.powered_off or vm.state == MachineState.aborted: # 1 # 4
94 | actions.append(Action("startvm", "Start virtual machine", lambda m=vm: startVm(m)))
95 | if vm.state == MachineState.saved: # 2
96 | actions.append(Action("restorevm", "Start saved virtual machine", lambda m=vm: startVm(m)))
97 | actions.append(Action("discardvm", "Discard saved state", lambda m=vm: discardSavedVm(m)))
98 | if vm.state == MachineState.running: # 5
99 | actions.append(Action("savevm", "Save virtual machine", lambda m=vm: saveVm(m)))
100 | actions.append(Action("poweroffvm", "Power off via ACPI event (Power button)", lambda m=vm: acpiPowerVm(m)))
101 | actions.append(Action("stopvm", "Turn off virtual machine", lambda m=vm: stopVm(m)))
102 | actions.append(Action("pausevm", "Pause virtual machine", lambda m=vm: pauseVm(m)))
103 | if vm.state == MachineState.paused: # 6
104 | actions.append(Action("resumevm", "Resume virtual machine", lambda m=vm: resumeVm(m)))
105 |
106 | items.append(
107 | StandardItem(
108 | id=vm.__uuid__,
109 | text=vm.name,
110 | subtext="{vm.state}".format(vm=vm),
111 | inputActionText=vm.name,
112 | iconUrls=self.iconUrls,
113 | actions=actions
114 | )
115 | )
116 | except Exception as e:
117 | warning(str(e))
118 |
119 | query.add(items)
120 |
--------------------------------------------------------------------------------
/pacman/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (c) 2024 Manuel Schneider
3 |
4 | import subprocess
5 | from time import sleep
6 | import pathlib
7 |
8 | from albert import Action, StandardItem, PluginInstance, TriggerQueryHandler, runTerminal, openUrl
9 |
10 | md_iid = "3.0"
11 | md_version = "2.0"
12 | md_name = "PacMan"
13 | md_description = "Search, install and remove packages"
14 | md_license = "MIT"
15 | md_url = "https://github.com/albertlauncher/python/tree/main/pacman"
16 | md_authors = "@ManuelSchneid3r"
17 | md_bin_dependencies = ["pacman", "expac"]
18 |
19 |
20 | class Plugin(PluginInstance, TriggerQueryHandler):
21 |
22 | pkgs_url = "https://www.archlinux.org/packages/"
23 |
24 | def __init__(self):
25 | PluginInstance.__init__(self)
26 | TriggerQueryHandler.__init__(self)
27 | self.iconUrls = [
28 | "xdg:archlinux-logo",
29 | "xdg:system-software-install",
30 | f"file:{pathlib.Path(__file__).parent}/arch.svg"
31 | ]
32 |
33 | def synopsis(self, query):
34 | return ""
35 |
36 | def defaultTrigger(self):
37 | return "pac "
38 |
39 | def handleTriggerQuery(self, query):
40 | stripped = query.string.strip()
41 |
42 | # Update item on empty queries
43 | if not stripped:
44 | query.add(StandardItem(
45 | id="%s-update" % self.id,
46 | text="Pacman package manager",
47 | subtext="Enter the package you are looking for or hit enter to update.",
48 | iconUrls=self.iconUrls,
49 | actions=[
50 | Action("up-nc", "Update packages (no confirm)",
51 | lambda: runTerminal("sudo pacman -Syu --noconfirm")),
52 | Action("up", "Update packages", lambda: runTerminal("sudo pacman -Syu")),
53 | Action("up-cache", "Update pacman cache", lambda: runTerminal("sudo pacman -Sy"))
54 | ]
55 | ))
56 | return
57 |
58 | # avoid rate limiting
59 | for _ in range(50):
60 | sleep(0.01)
61 | if not query.isValid:
62 | return
63 |
64 | # Get data. Results are sorted, so we can merge in O(n)
65 | proc_s = subprocess.Popen(["expac", "-Ss", "%n\t%v\t%r\t%d\t%u\t%E", stripped],
66 | stdout=subprocess.PIPE, universal_newlines=True)
67 | proc_q = subprocess.Popen(["expac", "-Qs", "%n", stripped], stdout=subprocess.PIPE, universal_newlines=True)
68 | proc_q.wait()
69 |
70 | items = []
71 | local_pkgs = set(proc_q.stdout.read().split('\n'))
72 | remote_pkgs = [tuple(line.split('\t')) for line in proc_s.stdout.read().split('\n')[:-1]] # newline at end
73 |
74 | for pkg_name, pkg_vers, pkg_repo, pkg_desc, pkg_purl, pkg_deps in remote_pkgs:
75 | if stripped not in pkg_name:
76 | continue
77 |
78 | pkg_installed = True if pkg_name in local_pkgs else False
79 |
80 | actions = []
81 | if pkg_installed:
82 | actions.extend([
83 | Action("rem", "Remove", lambda n=pkg_name: runTerminal("sudo pacman -Rs %s" % n)),
84 | Action("reinst", "Reinstall", lambda n=pkg_name: runTerminal("sudo pacman -S %s" % n))
85 | ])
86 | else:
87 | actions.append(Action("inst", "Install", lambda n=pkg_name: runTerminal("sudo pacman -S %s" % n)))
88 |
89 | actions.append(Action("pkg_url", "Show on packages.archlinux.org",
90 | lambda r=pkg_repo, n=pkg_name: openUrl(f"{self.pkgs_url}{r}/x86_64/{n}/")))
91 | if pkg_purl:
92 | actions.append(Action("proj_url", "Show project website", lambda u=pkg_purl: openUrl(u)))
93 |
94 | item = StandardItem(
95 | id="%s_%s_%s" % (self.id, pkg_repo, pkg_name),
96 | iconUrls=self.iconUrls,
97 | text="%s %s [%s]" % (pkg_name, pkg_vers, pkg_repo),
98 | subtext=f"{pkg_desc} [Installed]" if pkg_installed else f"{pkg_desc}",
99 | inputActionText="%s%s" % (query.trigger, pkg_name),
100 | actions=actions
101 | )
102 | items.append(item)
103 |
104 | if items:
105 | query.add(items)
106 | else:
107 | query.add(StandardItem(
108 | id="%s-empty" % self.id,
109 | text="Search on archlinux.org",
110 | subtext="No results found in the local database",
111 | iconUrls=self.iconUrls,
112 | actions=[Action("search", "Search on archlinux.org", lambda: openUrl(f"{self.pkgs_url}?q={stripped}"))]
113 | ))
114 |
--------------------------------------------------------------------------------
/.archive/multi_google_translate/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Use Google Translate to translate your sentence into multiple languages.
4 |
5 | Visit the following link to check available languages: \
6 | https://cloud.google.com/translate/docs/languages. To add or remove languages use modifier key \
7 | when trigger is activated or go to: '~/.config/albert/org.albert.extension.mtr/config.json' \
8 | Add or remove elements based on the ISO-Codes that you found on the google documentation page.
9 |
10 | Synopsis: [query]"""
11 |
12 | # Copyright (c) 2022 Manuel Schneider
13 |
14 | import json
15 | import os
16 | import urllib.error
17 | import urllib.parse
18 | import urllib.request
19 | from time import sleep
20 |
21 | from albert import (ClipAction, Item, ProcAction, UrlAction, configLocation,
22 | iconLookup)
23 |
24 | __title__ = "MultiTranslate"
25 | __version__ = "0.4.2"
26 | __triggers__ = "mtr "
27 | __authors__ = "David Britt"
28 |
29 | iconPath = iconLookup('config-language')
30 | if not iconPath:
31 | iconPath = ":python_module"
32 |
33 | ua = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36"
34 | urltmpl = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=%s&dt=t&q=%s"
35 | urlbrowser = "https://translate.google.com/#auto/%s/%s"
36 | configurationFileName = "language_config.json"
37 | configuration_directory = os.path.join(configLocation(), __title__)
38 | language_configuration_file = os.path.join(configuration_directory, configurationFileName)
39 | languages = []
40 |
41 | def initialize():
42 | if os.path.exists(language_configuration_file):
43 | with open(language_configuration_file) as json_config:
44 | languages.extend(json.load(json_config)["languages"])
45 | else:
46 | languages.extend(["en", "zh-CN", "hi", "es", "ru", "pt", "id", "bn", "ar", "ms", "ja", "fr", "de"])
47 | try:
48 | os.makedirs(configuration_directory, exist_ok=True)
49 | try:
50 | with open(language_configuration_file, "w") as output_file:
51 | json.dump({"languages": languages}, output_file)
52 | except OSError:
53 | print("There was an error opening the file: %s" % language_configuration_file)
54 | except OSError:
55 | print("There was an error making the directory: %s" % configuration_directory)
56 |
57 |
58 | def handleQuery(query):
59 | results = []
60 | if query.isTriggered:
61 |
62 | # avoid rate limiting
63 | sleep(0.2)
64 | if not query.isValid:
65 | return
66 |
67 | item = Item(
68 | id=__title__,
69 | icon=iconPath,
70 | text=__title__,
71 | actions=[ProcAction("Open the language configuration file.",
72 | commandline=["xdg-open", language_configuration_file])]
73 | )
74 | if len(query.string) >= 2:
75 | for lang in languages:
76 | try:
77 | url = urltmpl % (lang, urllib.parse.quote_plus(query.string))
78 | req = urllib.request.Request(url, headers={'User-Agent': ua})
79 | with urllib.request.urlopen(req) as response:
80 | #print(type())
81 | #try:
82 | data = json.loads(response.read().decode())
83 | #except TypeError as typerr:
84 | # print("Urgh this type.error. %s" % typerr)
85 | translText = data[0][0][0]
86 | sourceText = data[2]
87 | if sourceText == lang:
88 | continue
89 | else:
90 | results.append(
91 | Item(
92 | id=__title__,
93 | icon=iconPath,
94 | text="%s" % (translText),
95 | subtext="%s" % lang.upper(),
96 | actions=[
97 | ClipAction("Copy translation to clipboard", translText),
98 | UrlAction("Open in your Browser", urlbrowser % (lang, query.string))
99 | ]
100 | )
101 | )
102 | except urllib.error.URLError as urlerr :
103 | print("Check your internet connection: %s" % urlerr)
104 | item.subtext = "Check your internet connection."
105 | return item
106 | else:
107 | item.subtext = "Enter a query: 'mtr <text>'. Languages {%s}" % ", ".join(languages)
108 | return item
109 | return results
110 |
--------------------------------------------------------------------------------
/.archive/timer/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # # Copyright (c) 2018-2024 Manuel Schneider
3 | # # Copyright (c) 2020 Andreas Dominik Preikschat
4 |
5 | """
6 | Takes arguments in the form of '`[[hrs:]mins:]secs [name]`'. Empty fields resolve to `0`. \
7 | Fields exceeding the maximum amount of the time interval are automatically refactorized.
8 |
9 | Examples:
10 | - `5:` starts a 5 minutes timer
11 | - `1:: ` starts a 1 hour timer
12 | - `120:` starts a 2 hours timer
13 | """
14 |
15 | import threading
16 | from datetime import timedelta
17 | from pathlib import Path
18 | from time import strftime, time, localtime
19 |
20 | from albert import *
21 |
22 | md_iid = '2.3'
23 | md_version = "1.8"
24 | md_name = "Timer"
25 | md_description = "Set up timers"
26 | md_license = "MIT"
27 | md_url = "https://github.com/albertlauncher/python/tree/main/timer"
28 | md_authors = ["@manuelschneid3r", "@googol42"]
29 |
30 |
31 | class Timer(threading.Timer):
32 |
33 | def __init__(self, interval, name, callback):
34 | super().__init__(interval=interval,
35 | function=lambda: callback(self))
36 | self.name = name
37 | self.begin = int(time())
38 | self.end = self.begin + interval
39 | self.start()
40 |
41 |
42 | class Plugin(PluginInstance, TriggerQueryHandler):
43 |
44 | def __init__(self):
45 | TriggerQueryHandler.__init__(self,
46 | id=md_id,
47 | name=md_name,
48 | description=md_description,
49 | synopsis='[[hrs:]mins:]secs [name]',
50 | defaultTrigger='timer ')
51 | PluginInstance.__init__(self)
52 | self.iconUrls = [f"file:{Path(__file__).parent}/time.svg"]
53 | self.soundPath = Path(__file__).parent / "bing.wav"
54 | self.timers = []
55 | self.notification = None
56 |
57 | def finalize(self):
58 | for timer in self.timers:
59 | timer.cancel()
60 | self.timers.clear()
61 |
62 | def startTimer(self, interval, name):
63 | self.timers.append(Timer(interval, name, self.onTimerTimeout))
64 |
65 | def deleteTimer(self, timer):
66 | self.timers.remove(timer)
67 | timer.cancel()
68 |
69 | def onTimerTimeout(self, timer):
70 | self.notification = Notification(
71 | title=f"Timer '{timer.name if timer.name else 'Timer'}'",
72 | body=f"Timed out at {strftime('%X', localtime(timer.end))}"
73 | )
74 | self.deleteTimer(timer)
75 |
76 | def configWidget(self):
77 | return [
78 | {
79 | 'type': 'label',
80 | 'text': __doc__.strip(),
81 | 'widget_properties': { 'textFormat': 'Qt::MarkdownText' }
82 | }
83 | ]
84 |
85 | def handleTriggerQuery(self, query):
86 | if not query.isValid:
87 | return
88 |
89 | if query.string.strip():
90 | args = query.string.strip().split(maxsplit=1)
91 | fields = args[0].split(":")
92 | name = args[1] if 1 < len(args) else ''
93 | if not all(field.isdigit() or field == '' for field in fields):
94 | return StandardItem(
95 | id=self.name,
96 | text="Invalid input",
97 | subtext="Enter a query in the form of '%s[[hours:]minutes:]seconds [name]'" % self.defaultTrigger(),
98 | iconUrls=self.iconUrls,
99 | )
100 |
101 | seconds = 0
102 | fields.reverse()
103 | for i in range(len(fields)):
104 | seconds += int(fields[i] if fields[i] else 0)*(60**i)
105 |
106 | query.add(StandardItem(
107 | id=self.name,
108 | text=str(timedelta(seconds=seconds)),
109 | subtext='Set a timer with name "%s"' % name if name else 'Set a timer',
110 | iconUrls=self.iconUrls,
111 | actions=[Action("set-timer", "Set timer", lambda sec=seconds: self.startTimer(sec, name))]
112 | ))
113 | return
114 |
115 | # List timers
116 | items = []
117 | for timer in self.timers:
118 | m, s = divmod(timer.interval, 60)
119 | h, m = divmod(m, 60)
120 | identifier = "%d:%02d:%02d" % (h, m, s)
121 |
122 | timer_name_with_quotes = '"%s"' % timer.name if timer.name else ''
123 | items.append(StandardItem(
124 | id=self.name,
125 | text='Delete timer %s [%s]' % (timer_name_with_quotes, identifier),
126 | subtext="Times out %s" % strftime("%X", localtime(timer.end)),
127 | iconUrls=self.iconUrls,
128 | actions=[Action("delete-timer", "Delete timer", lambda t=timer: self.deleteTimer(t))]
129 | ))
130 |
131 | if items:
132 | query.add(items)
133 |
--------------------------------------------------------------------------------