├── .gitignore ├── README.md ├── albert_trigger.sh ├── cheat-sh.py ├── github.py ├── google.py ├── icons ├── GitHub.png ├── Google.png ├── GoogleTranslate.png ├── LearnAnything.png ├── Wordreference.png ├── YouTube.png └── python.svg ├── learn-anything.py ├── sed.py ├── wordreference-enit.py └── youtube.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Albert Plugins 2 | This is a collection of plugins I made for Albert launcher. 3 | 4 | ![img](https://thumbs.gfycat.com/BlondLinedGnatcatcher-max-14mb.gif) 5 | 6 | 7 | ## Requirements 8 | `lxml` is required for the Google plugin. It is used for parsing the results 9 | pages. It can be installed by running: 10 | 11 | ```bash 12 | pip install lxml 13 | ``` 14 | 15 | The `albert_trigger.sh` script requires xdotool, this script is used to open 16 | albert with a certain query/trigger already in it. 17 | 18 | 19 | ## Descriptions 20 | Here's a list of the plugins and a description of how they work. 21 | 22 | **Google (gg)** 23 | Get suggestions like the ones you get when searching on Google. When you select 24 | an item the corresponding results page is opened in your default browser. If you 25 | append `_` to your query you'll get the results directly on albert 26 | (eg: `gg unixporn reddit_`), and selecting any of the results will open it with 27 | your default browser. 28 | 29 | **YouTube (yt)** 30 | Same as above but it's for YouTube. When searching with an underscore after 31 | your query (eg: `yt panda dub_`) you get a list of videos as results, and 32 | selecting any one of them will open that video with your default browser. 33 | 34 | **Learn Anything (la)** 35 | Get suggestions from [Learn Anything](https://learn-anything.xyz). For now you 36 | only get the suggestions, but I plan on adding something similar to YouTube, 37 | where you get a list of nodes/resources for each map when you select it. 38 | 39 | **GitHub (gh)** 40 | Search for a GitHub repo. 41 | 42 | **Wordreference (enit)** 43 | Get suggestions from Wordreference, I use it mostly when I'm not sure how to 44 | spell a word, but if you select a word you can search for the translation. For 45 | now it's only English to Italian or vice-versa, but I think it would be cool to 46 | be able to set that in a config or just specify it on the query. 47 | 48 | 49 | ## Keybindings 50 | You can find my keybindings on my [i3 config](https://github.com/nglgzz/dots/blob/laptop/config/i3/config) 51 | but I'll go over the ones regarding Albert here. I remapped my spacebar to be 52 | spacebar when pressed on its own, but Mod3 when pressed together with any other 53 | key. This is accomplished by adding the following to `.xmodmaprc`: 54 | 55 | ``` 56 | keycode 65 = XF86Mail 57 | keycode any = space 58 | add mod3 = XF86Mail 59 | ``` 60 | 61 | You can use anything you want instead of `XF86Mail`, just check that it's a key 62 | that you don't use. After that on your `.xinitrc` you should have two lines like 63 | these: 64 | 65 | ``` 66 | xmodmap ~/.xmodmaprc 67 | xcape -e "XF86Mail=space" 68 | ``` 69 | 70 | `xcape` is the program doing the magic I described before, and [here's](https://www.reddit.com/r/i3wm/comments/5zpz69/using_space_bar_as_mod_is_life_changing/) 71 | a reddit post going more in detail on why you should use spacebar as a modifier. 72 | 73 | The keybindings I have for these plugins are just calling `albert_trigger.sh` with 74 | the trigger to use each plugin. 75 | 76 | Here's the list of keybindings: 77 | 78 | - **Space + b**: Toggle Albert 79 | - **Space + g**: Google 80 | - **Space + h**: GitHub 81 | - **Space + y**: Youtube 82 | - **Space + a**: Learn Anything 83 | - **Space + e**: Wordreference 84 | -------------------------------------------------------------------------------- /albert_trigger.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | albert hide 3 | albert show 4 | sleep 0.1 5 | xdotool type "$1" 6 | -------------------------------------------------------------------------------- /cheat-sh.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get suggestions for Learn Anything 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import json 8 | import re 9 | 10 | 11 | __iid__ = 'PythonInterface/v0.1' 12 | __prettyname__ = 'Cheat.sh' 13 | __version__ = '1.0' 14 | __trigger__ = 'ch ' 15 | __author__ = 'Angelo Gazzola' 16 | __dependencies__ = [] 17 | __icon__ = path.dirname(__file__) + '/icons/python.svg' 18 | 19 | 20 | COLORS_PATTERN = re.compile(r'\[[\d;]*m'); 21 | REQUEST_HEADERS = { 22 | 'User-Agent': ( 23 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 24 | ' Chrome/62.0.3202.62 Safari/537.36' 25 | ) 26 | } 27 | session = requests.Session() 28 | session.trust_env = False 29 | 30 | 31 | def to_item(text, id): 32 | return Item( 33 | id=id, 34 | text=text, 35 | icon=__icon__, 36 | actions=[ 37 | ClipAction("Copy result to clipboard", text) 38 | ] 39 | ) 40 | 41 | def search(query): 42 | response = session.get("http://cheat.sh/{}".format(query)) 43 | text = COLORS_PATTERN.sub('', response.text) 44 | return [to_item(chunk, query + str(index)) 45 | for index, chunk in enumerate(text.split('\n\n'))] 46 | 47 | 48 | def handleQuery(query): 49 | if query.isTriggered and len(query.string) > 0: 50 | return search(query.string) 51 | return [Item(icon=__icon__, text='Cheat')] 52 | -------------------------------------------------------------------------------- /github.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search GitHub repos 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import json 8 | 9 | __iid__ = 'PythonInterface/v0.1' 10 | __prettyname__ = 'GitHub Repos' 11 | __version__ = '1.0' 12 | __trigger__ = 'gh ' 13 | __author__ = 'Angelo Gazzola' 14 | __dependencies__ = [] 15 | __icon__ = path.dirname(__file__) + '/icons/GitHub.png' 16 | 17 | 18 | REQUEST_HEADERS = { 19 | 'User-Agent': ( 20 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 21 | ' Chrome/62.0.3202.62 Safari/537.36' 22 | ) 23 | } 24 | session = requests.Session() 25 | session.trust_env = False 26 | 27 | 28 | def to_item(repo): 29 | description = repo["description"] 30 | 31 | if description and len(description) > 40: 32 | description = description[:40] + "..." 33 | 34 | subtext = "{} ({} issues - {} forks)".format( 35 | description, 36 | repo["open_issues"], 37 | repo["forks_count"] 38 | ) 39 | 40 | return Item( 41 | id=str(repo['id']), 42 | text=repo['full_name'], 43 | icon=__icon__, 44 | subtext=subtext, 45 | actions=[ 46 | UrlAction('View on Github', repo['html_url']), 47 | ClipAction('Copy clone url', repo['clone_url']), 48 | ] 49 | ) 50 | 51 | 52 | def search(query): 53 | response = session.get("https://api.github.com/search/repositories", 54 | headers=REQUEST_HEADERS, 55 | params={ 56 | "q": query, 57 | } 58 | ) 59 | 60 | if response.json().get('items'): 61 | repos = sorted( 62 | response.json()['items'], 63 | key=(lambda el: int(el["stargazers_count"])) 64 | ) 65 | 66 | return [to_item(repo) for repo in repos] 67 | return [] 68 | 69 | 70 | def handleQuery(query): 71 | if query.isTriggered and len(query.string) > 0: 72 | items = search(query.string) 73 | return items 74 | return [Item(icon=__icon__, text='GitHub repos')] 75 | -------------------------------------------------------------------------------- /google.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get suggestions from Google 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import lxml.html 8 | import json 9 | 10 | 11 | __iid__ = 'PythonInterface/v0.1' 12 | __prettyname__ = 'Google Suggestions' 13 | __version__ = '1.0' 14 | __trigger__ = 'gg ' 15 | __author__ = 'Angelo Gazzola' 16 | __dependencies__ = ['lxml', 'cssselect'] 17 | __icon__ = path.dirname(__file__) + '/icons/Google.png' 18 | 19 | 20 | MAX_LINE_LENGTH = 75 21 | REQUEST_HEADERS = { 22 | 'User-Agent': ( 23 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 24 | ' Chrome/62.0.3202.62 Safari/537.36' 25 | ) 26 | } 27 | session = requests.Session() 28 | session.trust_env = False 29 | 30 | 31 | class SuggestionItem(Item): 32 | def __init__(self, suggestion): 33 | super().__init__( 34 | id=str(hash(suggestion)), 35 | icon=__icon__, 36 | text=suggestion, 37 | completion=__trigger__ + suggestion + '_', 38 | actions=[ 39 | UrlAction( 40 | 'Search on Google', 41 | 'https://google.com/search?q={}'.format(suggestion) 42 | ) 43 | ] 44 | ) 45 | 46 | 47 | class ResultItem(Item): 48 | def __init__(self, result, query): 49 | super().__init__( 50 | id=result[1], 51 | icon=__icon__, 52 | text=result[0], 53 | subtext=result[1] + '\n' + result[2], 54 | completion=__trigger__ + query + '_', 55 | actions=[UrlAction('Search on Google', result[1])] 56 | ) 57 | 58 | 59 | def search(query): 60 | response = session.get('https://google.com/search', 61 | headers=REQUEST_HEADERS, 62 | params={ 63 | 'sourceid': 'chrome', 64 | 'hl': 'en', 65 | 'q': query, 66 | } 67 | ) 68 | 69 | html = lxml.html.fromstring(response.text) 70 | results = html.cssselect('.rc') 71 | items = [] 72 | 73 | for result in results: 74 | title = result.cssselect('h3 > a')[0] 75 | url = title.get('href') 76 | 77 | description = result.cssselect('.s .st') 78 | formatted_description = [] 79 | 80 | if len(description) > 0: 81 | description = description[0].text_content() 82 | 83 | for i in range(MAX_LINE_LENGTH, len(description), MAX_LINE_LENGTH): 84 | formatted_description.append(description[i-MAX_LINE_LENGTH:i]) 85 | formatted_description.append(description[i:-1]) 86 | 87 | items.append((title.text, url, '\n'.join(formatted_description))) 88 | 89 | return [ResultItem(r, query) for r in items] 90 | 91 | 92 | def suggest(query): 93 | response = session.get('https://clients1.google.com/complete/search', 94 | headers=REQUEST_HEADERS, 95 | params={ 96 | 'client': 'firefox', 97 | 'output': 'toolbar', 98 | 'hl': 'en', 99 | 'q': query, 100 | } 101 | ) 102 | suggestions = json.loads(response.text)[1] 103 | return [SuggestionItem(suggestion) for suggestion in suggestions] 104 | 105 | 106 | def handleQuery(query): 107 | if query.isTriggered and len(query.string) > 0: 108 | if query.string[-1] == '_': 109 | items = search(query.string[:-1]) 110 | else: 111 | items = suggest(query.string) 112 | items.insert(0, SuggestionItem(query.string)) 113 | return items 114 | 115 | return [Item(icon=__icon__, text='Google')] 116 | -------------------------------------------------------------------------------- /icons/GitHub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/GitHub.png -------------------------------------------------------------------------------- /icons/Google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/Google.png -------------------------------------------------------------------------------- /icons/GoogleTranslate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/GoogleTranslate.png -------------------------------------------------------------------------------- /icons/LearnAnything.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/LearnAnything.png -------------------------------------------------------------------------------- /icons/Wordreference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/Wordreference.png -------------------------------------------------------------------------------- /icons/YouTube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nglgzz-archive/albert-plugins/bf515d4807985ed69a4b5e9aecd7549acec1636d/icons/YouTube.png -------------------------------------------------------------------------------- /icons/python.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /learn-anything.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get suggestions for Learn Anything 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import json 8 | 9 | __iid__ = 'PythonInterface/v0.1' 10 | __prettyname__ = 'Learn Anything Suggestions' 11 | __version__ = '1.0' 12 | __trigger__ = 'la ' 13 | __author__ = 'Angelo Gazzola' 14 | __dependencies__ = [] 15 | __icon__ = path.dirname(__file__) + '/icons/LearnAnything.png' 16 | 17 | 18 | REQUEST_HEADERS = { 19 | 'User-Agent': ( 20 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 21 | ' Chrome/62.0.3202.62 Safari/537.36' 22 | ) 23 | } 24 | session = requests.Session() 25 | session.trust_env = False 26 | 27 | 28 | def to_item(suggestion): 29 | return Item( 30 | id=suggestion['id'], 31 | text=suggestion['key'], 32 | icon=__icon__, 33 | actions=[ 34 | UrlAction('Search on Learn Anything', 'https://learn-anything.xyz/{}'.format(suggestion['id'])), 35 | ] 36 | ) 37 | 38 | 39 | def search(query): 40 | response = session.get("https://learn-anything.xyz/api/maps", params={ 41 | "q": query, 42 | }) 43 | suggestions = json.loads(response.text) 44 | return [to_item(suggestion) for suggestion in suggestions] 45 | 46 | 47 | def handleQuery(query): 48 | if query.isTriggered and len(query.string) > 0: 49 | items = search(query.string) 50 | return items 51 | return [Item(icon=__icon__, text='I want to learn')] 52 | -------------------------------------------------------------------------------- /sed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Evaluate simple python expressions. 4 | Use it with care every keystroke triggers an evaluation.""" 5 | 6 | from albertv0 import * 7 | from math import * 8 | import json 9 | import re 10 | from builtins import pow 11 | import clipboard 12 | 13 | try: 14 | import numpy as np 15 | except ImportError: 16 | pass 17 | import os 18 | 19 | __iid__ = "PythonInterface/v0.1" 20 | __prettyname__ = "Python Eval" 21 | __version__ = "1.0" 22 | __trigger__ = "sed " 23 | __author__ = "Angelo Gazzola" 24 | __dependencies__ = ["clipboard"] 25 | 26 | iconPath = os.path.dirname(__file__)+"/icons/python.svg" 27 | cl = '' 28 | 29 | def handleQuery(query): 30 | def sed(pattern='', replace=''): 31 | return re.sub(re.compile(pattern, re.MULTILINE), replace, cl) 32 | 33 | if query.isTriggered: 34 | cl = clipboard.paste() 35 | item = Item(id=__prettyname__, completion=query.rawString) 36 | stripped = query.string.strip() 37 | 38 | if stripped == '': 39 | item.text = "Enter pattern - substitute" 40 | item.subtext = "Replace stuff with regex <3" 41 | return [item] 42 | else: 43 | try: 44 | result = stripped.split(' - ') 45 | 46 | if len(result) == 2: 47 | result = sed(result[0], result[1]) 48 | else: 49 | result = sed(result[0]) 50 | #result = eval(stripped) 51 | except Exception as ex: 52 | result = ex 53 | item.text = str(result) 54 | item.subtext = type(result).__name__ 55 | item.addAction(ClipAction("Copy result to clipboard", str(result))) 56 | item.addAction(FuncAction("Execute", lambda: exec(str(result)))) 57 | return [item] 58 | -------------------------------------------------------------------------------- /wordreference-enit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Wordreference autocompletion (enit) 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import lxml.html 8 | import json 9 | 10 | 11 | __iid__ = 'PythonInterface/v0.1' 12 | __prettyname__ = 'Wordreference autocompletion (enit)' 13 | __version__ = '1.0' 14 | __trigger__ = 'enit ' 15 | __author__ = 'Angelo Gazzola' 16 | __dependencies__ = [] 17 | __icon__ = path.dirname(__file__) + '/icons/Wordreference.png' 18 | __lang__ = 'enit' 19 | 20 | REQUEST_HEADERS = { 21 | 'User-Agent': ( 22 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 23 | ' Chrome/62.0.3202.62 Safari/537.36' 24 | ) 25 | } 26 | session = requests.Session() 27 | session.trust_env = False 28 | 29 | iconPath = path.dirname(__file__) + '/icons/Wordreference.png' 30 | 31 | 32 | class SuggestionItem(Item): 33 | def __init__(self, suggestion): 34 | super().__init__( 35 | id=str(hash(suggestion)), 36 | icon=__icon__, 37 | text=suggestion, 38 | completion=__trigger__ + suggestion + '_', 39 | actions=[ 40 | UrlAction( 41 | 'Search on Wordreference', 42 | 'http://www.wordreference.com/{}/{}'.format(__lang__, suggestion) 43 | ) 44 | ] 45 | ) 46 | 47 | 48 | class ResultItem(Item): 49 | def __init__(self, result): 50 | super().__init__( 51 | id=str(hash(str(result[0]) + str(result[1]) + str(result[2]))), 52 | icon=__icon__, 53 | text=result[2], 54 | subtext='{} | {} | {}'.format(result[0], result[1], result[2]) 55 | ) 56 | 57 | 58 | def search(query): 59 | response = session.get( 60 | "http://www.wordreference.com/{}/{}".format(__lang__, query), 61 | headers=REQUEST_HEADERS 62 | ) 63 | html = lxml.html.fromstring(response.text) 64 | selections = html.cssselect('tr[id^="{}"]'.format(__lang__)) 65 | selections += html.cssselect('tr[id^="iten"]') 66 | results = [] 67 | 68 | for sel in selections: 69 | sel = sel.getchildren() 70 | results.append(( 71 | sel[0].find('strong').text, 72 | sel[1].text, 73 | sel[2].text 74 | )) 75 | 76 | return [ResultItem(r) for r in results] 77 | 78 | 79 | def suggest(query): 80 | response = session.get("http://www.wordreference.com/2012/autocomplete/autocomplete.aspx", 81 | headers=REQUEST_HEADERS, 82 | params={ 83 | "dict": __lang__, 84 | "query": query, 85 | } 86 | ) 87 | suggestions = response.text.split('\n') 88 | return [SuggestionItem(suggestion.split('\t')[0]) for suggestion in suggestions if suggestion.split('\t')[0]] 89 | 90 | 91 | def handleQuery(query): 92 | if query.isTriggered and len(query.string) > 0: 93 | if query.string[-1] == '_': 94 | items = search(query.string[:-1]) 95 | else: 96 | items = suggest(query.string) 97 | items.insert(0, SuggestionItem(query.string)) 98 | return items 99 | return [Item(icon=__icon__, text='Wordreference [enit]')] 100 | -------------------------------------------------------------------------------- /youtube.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get suggestions from Youtube 3 | """ 4 | from albertv0 import * 5 | from os import path 6 | import requests 7 | import json 8 | import re 9 | 10 | 11 | __iid__ = 'PythonInterface/v0.1' 12 | __prettyname__ = 'Youtube Suggestions' 13 | __version__ = '1.0' 14 | __trigger__ = 'yt ' 15 | __author__ = 'Angelo Gazzola' 16 | __dependencies__ = [] 17 | __icon__ = path.dirname(__file__) + '/icons/YouTube.png' 18 | 19 | 20 | REQUEST_HEADERS = { 21 | 'User-Agent': ( 22 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)' 23 | ' Chrome/62.0.3202.62 Safari/537.36' 24 | ) 25 | } 26 | session = requests.Session() 27 | session.trust_env = False 28 | re_videos = re.compile(r'"contents":(\[{"videoRenderer":.*}\]),"continuations"') 29 | 30 | 31 | class SuggestionItem(Item): 32 | def __init__(self, suggestion): 33 | super().__init__( 34 | id=str(hash(suggestion)), 35 | icon=__icon__, 36 | text=suggestion, 37 | completion=__trigger__ + suggestion + '_', 38 | actions=[ 39 | UrlAction( 40 | 'Search on YouTube', 41 | 'https://youtube.com/search?q={}'.format(suggestion) 42 | ) 43 | ] 44 | ) 45 | 46 | class ResultItem(Item): 47 | def __init__(self, video, query): 48 | try: 49 | text = video['title']['simpleText'] 50 | subtext = '{} \t| {} | {}'.format( 51 | video['shortViewCountText']['simpleText'], 52 | video['lengthText']['simpleText'], 53 | video['ownerText']['runs'][0]['text'], 54 | ) 55 | actions = [ 56 | UrlAction('Watch on Youtube', 'https://youtube.com/watch?v={}'.format(video['videoId'])) 57 | ] 58 | 59 | super().__init__( 60 | id=video['videoId'], 61 | text=text, 62 | icon=__icon__, 63 | completion=__trigger__ + query + '_', 64 | subtext=subtext, 65 | actions=actions 66 | ) 67 | except: 68 | debug(json.dumps(video)) 69 | 70 | 71 | def search(query): 72 | response = session.get('https://www.youtube.com/results', 73 | headers=REQUEST_HEADERS, 74 | params={ 75 | 'search_query': query, 76 | } 77 | ) 78 | match = re_videos.search(response.text) 79 | 80 | if not match: 81 | debug(query) 82 | return [Item(text='No results found')] 83 | 84 | videos = json.loads(match.groups()[0]) 85 | return [ResultItem(video['videoRenderer'], query) for video in videos if video.get('videoRenderer')] 86 | 87 | 88 | def complete(query): 89 | response = session.get('https://clients1.google.com/complete/search', 90 | headers=REQUEST_HEADERS, 91 | params={ 92 | 'client': 'youtube', 93 | 'output': 'toolbar', 94 | 'hl': 'en', 95 | 'q': query, 96 | } 97 | ) 98 | response = response.text.lstrip('window.google.ac.h(').rstrip(')') 99 | suggestions = json.loads(response)[1] 100 | return [SuggestionItem(suggestion[0]) for suggestion in suggestions] 101 | 102 | 103 | def handleQuery(query): 104 | if query.isTriggered and len(query.string) > 0: 105 | if query.string[-1] == '_': 106 | items = search(query.string[:-1]) 107 | else: 108 | items = complete(query.string) 109 | items.insert(0, SuggestionItem(query.string)) 110 | return items 111 | 112 | return [Item(icon=__icon__, text='YouTube')] 113 | 114 | --------------------------------------------------------------------------------