commit
7c3f545438
@ -30,6 +30,7 @@ MOCK_MODULES = [
|
||||
"i3pystatus.pulseaudio.pulse",
|
||||
"notmuch",
|
||||
"requests",
|
||||
"bs4"
|
||||
]
|
||||
|
||||
for mod_name in MOCK_MODULES:
|
||||
|
@ -439,7 +439,16 @@ def user_open(url_or_command):
|
||||
scheme = urlparse(url_or_command).scheme
|
||||
if scheme == 'http' or scheme == 'https':
|
||||
import webbrowser
|
||||
import os
|
||||
# webbrowser.open() sometimes prints a message for some reason and confuses i3
|
||||
# Redirect stdout briefly to prevent this from happening.
|
||||
savout = os.dup(1)
|
||||
os.close(1)
|
||||
os.open(os.devnull, os.O_RDWR)
|
||||
try:
|
||||
webbrowser.open(url_or_command)
|
||||
finally:
|
||||
os.dup2(savout, 1)
|
||||
else:
|
||||
import subprocess
|
||||
subprocess.Popen(url_or_command, shell=True)
|
||||
|
59
i3pystatus/github.py
Normal file
59
i3pystatus/github.py
Normal file
@ -0,0 +1,59 @@
|
||||
from i3pystatus import IntervalModule
|
||||
import requests
|
||||
import json
|
||||
from i3pystatus.core import ConfigError
|
||||
from i3pystatus.core.util import user_open, internet, require
|
||||
|
||||
|
||||
class Github(IntervalModule):
|
||||
"""
|
||||
Check Github for pending notifications.
|
||||
Requires `requests`
|
||||
|
||||
Formatters:
|
||||
|
||||
* `{unread}` - contains the value of unread_marker when there are pending notifications
|
||||
* `{unread_count}` - number of unread notifications, empty if 0
|
||||
"""
|
||||
|
||||
unread_marker = u"●"
|
||||
unread = ''
|
||||
color = '#78EAF2'
|
||||
username = ''
|
||||
password = ''
|
||||
format = '{unread}'
|
||||
interval = 600
|
||||
|
||||
on_leftclick = 'open_github'
|
||||
|
||||
settings = (
|
||||
('format', 'format string'),
|
||||
('unread_marker', 'sets the string that the "unread" formatter shows when there are pending notifications'),
|
||||
("username", ""),
|
||||
("password", ""),
|
||||
("color", "")
|
||||
)
|
||||
|
||||
def open_github(self):
|
||||
user_open('https://github.com/' + self.username)
|
||||
|
||||
@require(internet)
|
||||
def run(self):
|
||||
format_values = dict(unread_count='', unread='')
|
||||
|
||||
response = requests.get('https://api.github.com/notifications', auth=(self.username, self.password))
|
||||
data = json.loads(response.text)
|
||||
|
||||
# Bad credentials
|
||||
if isinstance(data, dict):
|
||||
raise ConfigError(data['message'])
|
||||
|
||||
unread = len(data)
|
||||
if unread > 0:
|
||||
format_values['unread_count'] = unread
|
||||
format_values['unread'] = self.unread_marker
|
||||
|
||||
self.output = {
|
||||
'full_text': self.format.format(**format_values),
|
||||
'color': self.color
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import netifaces
|
||||
import basiciw
|
||||
import psutil
|
||||
from i3pystatus import IntervalModule
|
||||
from i3pystatus.core.color import ColorRangeModule
|
||||
from i3pystatus.core.util import make_graph, round_dict, make_bar
|
||||
@ -74,7 +72,7 @@ class NetworkInfo():
|
||||
Retrieve network information.
|
||||
"""
|
||||
|
||||
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up):
|
||||
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up, get_wifi_info=False):
|
||||
if interface not in netifaces.interfaces() and not detached_down:
|
||||
raise RuntimeError(
|
||||
"Unknown interface {iface}!".format(iface=interface))
|
||||
@ -82,6 +80,7 @@ class NetworkInfo():
|
||||
self.ignore_interfaces = ignore_interfaces
|
||||
self.detached_down = detached_down
|
||||
self.unknown_up = unknown_up
|
||||
self.get_wifi_info = get_wifi_info
|
||||
|
||||
def get_info(self, interface):
|
||||
format_dict = dict(v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="")
|
||||
@ -133,9 +132,15 @@ class NetworkInfo():
|
||||
break
|
||||
return info
|
||||
|
||||
@staticmethod
|
||||
def extract_wireless_info(interface):
|
||||
def extract_wireless_info(self, interface):
|
||||
info = dict(essid="", freq="", quality=0.0, quality_bar="")
|
||||
|
||||
# Just return empty values if we're not using any Wifi functionality
|
||||
if not self.get_wifi_info:
|
||||
return info
|
||||
|
||||
import basiciw
|
||||
|
||||
try:
|
||||
iwi = basiciw.iwinfo(interface)
|
||||
except Exception:
|
||||
@ -169,6 +174,8 @@ class NetworkTraffic():
|
||||
self.round_size = round_size
|
||||
|
||||
def update_counters(self, interface):
|
||||
import psutil
|
||||
|
||||
self.pnic_before = self.pnic
|
||||
counters = psutil.net_io_counters(pernic=True)
|
||||
self.pnic = counters[interface] if interface in counters else None
|
||||
@ -282,22 +289,39 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
|
||||
on_leftclick = "nm-connection-editor"
|
||||
on_rightclick = "cycle_interface"
|
||||
on_upscroll = ['cycle_interface', 1]
|
||||
on_downscroll = ['cycle_interface', -1]
|
||||
|
||||
def init(self):
|
||||
# Don't require importing basiciw unless using the functionality it offers.
|
||||
if any(s in self.format_up or s in self.format_up for s in
|
||||
['essid', 'freq', 'quality', 'quality_bar']):
|
||||
get_wifi_info = True
|
||||
else:
|
||||
get_wifi_info = False
|
||||
|
||||
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up,
|
||||
get_wifi_info)
|
||||
|
||||
# Don't require importing psutil unless using the functionality it offers.
|
||||
if any(s in self.format_up or s in self.format_down for s in
|
||||
['bytes_sent', 'bytes_recv', 'packets_sent', 'packets_recv', 'network_graph']):
|
||||
self.network_traffic = NetworkTraffic(self.unknown_up, self.divisor, self.round_size)
|
||||
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up)
|
||||
else:
|
||||
self.network_traffic = None
|
||||
|
||||
self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit))
|
||||
self.kbs_arr = [0.0] * self.graph_width
|
||||
|
||||
def cycle_interface(self):
|
||||
def cycle_interface(self, increment=1):
|
||||
interfaces = [i for i in netifaces.interfaces() if i not in self.ignore_interfaces]
|
||||
if self.interface in interfaces:
|
||||
next_index = (interfaces.index(self.interface) + 1) % len(interfaces)
|
||||
next_index = (interfaces.index(self.interface) + increment) % len(interfaces)
|
||||
self.interface = interfaces[next_index]
|
||||
elif len(interfaces) > 0:
|
||||
self.interface = interfaces[0]
|
||||
|
||||
if self.network_traffic:
|
||||
self.network_traffic.clear_counters()
|
||||
self.kbs_arr = [0.0] * self.graph_width
|
||||
|
||||
@ -311,13 +335,10 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
||||
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
|
||||
essid="", freq="", quality="", quality_bar="")
|
||||
|
||||
color = None
|
||||
if self.network_traffic:
|
||||
network_usage = self.network_traffic.get_usage(self.interface)
|
||||
format_values.update(network_usage)
|
||||
|
||||
network_info = self.network_info.get_info(self.interface)
|
||||
format_values.update(network_info)
|
||||
|
||||
if self.graph_type == 'input':
|
||||
kbs = network_usage['bytes_recv']
|
||||
elif self.graph_type == 'output':
|
||||
@ -327,12 +348,14 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
|
||||
format_values['network_graph'] = self.get_network_graph(kbs)
|
||||
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
||||
format_values['interface'] = self.interface
|
||||
|
||||
if sysfs_interface_up(self.interface, self.unknown_up):
|
||||
if self.dynamic_color:
|
||||
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
||||
else:
|
||||
|
||||
network_info = self.network_info.get_info(self.interface)
|
||||
format_values.update(network_info)
|
||||
|
||||
format_values['interface'] = self.interface
|
||||
if sysfs_interface_up(self.interface, self.unknown_up):
|
||||
if not self.dynamic_color:
|
||||
color = self.color_up
|
||||
|
||||
self.output = {
|
||||
|
@ -62,7 +62,6 @@ class Reddit(IntervalModule):
|
||||
}
|
||||
|
||||
on_leftclick = "open_permalink"
|
||||
on_click = "open_link"
|
||||
|
||||
_permalink = ""
|
||||
_url = ""
|
||||
@ -136,6 +135,9 @@ class Reddit(IntervalModule):
|
||||
"color": color,
|
||||
}
|
||||
|
||||
def open_mail(self):
|
||||
user_open('https://www.reddit.com/message/unread/')
|
||||
|
||||
def open_permalink(self):
|
||||
user_open(self._permalink)
|
||||
|
||||
|
107
i3pystatus/whosonlocation.py
Normal file
107
i3pystatus/whosonlocation.py
Normal file
@ -0,0 +1,107 @@
|
||||
from i3pystatus import IntervalModule
|
||||
import requests
|
||||
from collections import OrderedDict
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
class WhosOnLocation():
|
||||
|
||||
email = None
|
||||
password = None
|
||||
session = None
|
||||
|
||||
def __init__(self, email, password):
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.session = requests.Session()
|
||||
|
||||
def login(self):
|
||||
login_details = {'email_input': self.email,
|
||||
'password_input': self.password,
|
||||
'_redirect_url': '',
|
||||
'continue_submit': 'Login'}
|
||||
r = self.session.post('https://login.whosonlocation.com/login', data=login_details)
|
||||
return r.url == 'https://au.whosonlocation.com/home?justloggedin=true'
|
||||
|
||||
def get_status(self):
|
||||
r = self.session.get('https://au.whosonlocation.com/home?justloggedin=true')
|
||||
html = BeautifulSoup(r.content)
|
||||
status = html.body.find("span", {"class": "my-status-name"})
|
||||
if status:
|
||||
return status.text
|
||||
|
||||
def on_site(self):
|
||||
return self.__change_status('onsite')
|
||||
|
||||
def off_site(self):
|
||||
return self.__change_status('offsite')
|
||||
|
||||
def __change_status(self, status):
|
||||
r = self.session.post('https://au.whosonlocation.com/ajax/changestatus', data={'status': status})
|
||||
return r.json()
|
||||
|
||||
# _type can be org or location
|
||||
def search(self, keyword, _type='location'):
|
||||
payload = {'keyword': keyword, 'type': _type}
|
||||
r = self.session.get('https://au.whosonlocation.com/home/search', params=payload)
|
||||
return self.__parse_results(BeautifulSoup(r.content))
|
||||
|
||||
@staticmethod
|
||||
def __parse_results(page):
|
||||
titles = ['Name', 'Title', 'Department', 'Current Location', 'Home Location']
|
||||
table = page.body.find_all("tr", {"class": "dataRow"})
|
||||
results = []
|
||||
for row in table:
|
||||
values = [v.string for v in row.findAll('td', {'class': 'truncate'})]
|
||||
results.append(OrderedDict(zip(titles, values)))
|
||||
return results
|
||||
|
||||
|
||||
class WOL(IntervalModule):
|
||||
"""
|
||||
Change your whosonlocation.com status.
|
||||
|
||||
Requires the PyPi module `beautifulsoup4`
|
||||
"""
|
||||
location = None
|
||||
email = None
|
||||
password = None
|
||||
|
||||
settings = (
|
||||
'email',
|
||||
'password'
|
||||
)
|
||||
|
||||
color_on_site = '#00FF00'
|
||||
color_off_site = '#ff0000'
|
||||
format = 'Status: {status}'
|
||||
status = None
|
||||
|
||||
on_leftclick = 'change_status'
|
||||
|
||||
def init(self):
|
||||
self.location = WhosOnLocation(self.email, self.password)
|
||||
if not self.location.login():
|
||||
raise Exception("Failed to login")
|
||||
|
||||
def change_status(self):
|
||||
if self.status == 'On-Site':
|
||||
self.location.off_site()
|
||||
elif self.status == 'Off-Site':
|
||||
self.location.on_site()
|
||||
|
||||
def run(self):
|
||||
self.status = self.location.get_status()
|
||||
color = None
|
||||
|
||||
if self.status == 'Off-Site':
|
||||
color = self.color_off_site
|
||||
elif self.status == 'On-Site':
|
||||
color = self.color_on_site
|
||||
|
||||
self.output = {
|
||||
"full_text": self.format.format(
|
||||
status=self.status
|
||||
),
|
||||
"color": color
|
||||
}
|
Loading…
Reference in New Issue
Block a user