Skip to content

Commit

Permalink
Add "group" support, where each group can have different recipients
Browse files Browse the repository at this point in the history
This update pushes the system from using a single http and sms endpoint
to using any number of endpoints. Messages can be tagged with a one or
more groups, and each group can have one or more endpoints associated
with it. This way users can send military or defensive alerts to their
alliance without spamming them with messages about local economic
conditions.
  • Loading branch information
tedivm committed Jun 25, 2017
1 parent 54cf8c4 commit da94864
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 107 deletions.
67 changes: 59 additions & 8 deletions .settings.dist.yaml
Expand Up @@ -5,14 +5,65 @@ screeps_username:
screeps_password:
screeps_ptr: false

# Your Account SID from www.twilio.com/console
twilio_sid:
services:

# Your Auth Token from www.twilio.com/console
twilio_token:
sms:
# Set driver to twilio
type: sms

# You SMS number from twilio. https://www.twilio.com/console/phone-numbers/dashboard
sms_from: '+15555555555'
# Your Account SID from www.twilio.com/console
twilio_sid:

# This should be the number you want to receive the texts.
sms_to: '+15555555555'
# Your Auth Token from www.twilio.com/console
twilio_token:

# You SMS number from twilio. https://www.twilio.com/console/phone-numbers/dashboard
sms_from: '+15555555555'

# This should be the number you want to receive the texts.
sms_to: '+15555555555'

alliance:
# Set driver to HTTP
type: http

# Specify a url
url:

# Provide an API key for AWS Gateway (optional)
api-key:

logs:
# Set driver to HTTP
type: http

# Specify a url
url:

# Provide a username for basic http authentication
http_user:

# Provide a password for basic http authentication
http_password:


slack:

# Set driver to slack
type: slack

# Get webhook from slack.
webhook_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX'


groups:

default:
- sms
- logs

economy:
- logs
- slack

defense: all
1 change: 0 additions & 1 deletion bin/screepsnotify.sh
Expand Up @@ -13,7 +13,6 @@ then
else
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
fi
cd $DIR/..

ENV="$DIR/../env/bin/activate"
if [ ! -f $ENV ]; then
Expand Down
20 changes: 13 additions & 7 deletions js/Notify.js
@@ -1,5 +1,9 @@

var Notify = function (message, limit) {
var Notify = function (message, limit=false, groups=false) {

if(!groups) {
groups = ['default']
}

// If no limit then send immediately (and potentially repeatedly)
if(!limit) {
Expand All @@ -9,13 +13,15 @@ var Notify = function (message, limit) {

// In cases where there are limits we have to record the history.

var queue_message = message + '::' + groups.join('_')

if(!Memory.__notify_history) {
Memory.__notify_history = {}
}

// If the message was sent in the last LIMIT ticks then don't send again.
if(!!Memory.__notify_history[message]) {
var lastSent = Memory.__notify_history[message]
if(!!Memory.__notify_history[queue_message]) {
var lastSent = Memory.__notify_history[queue_message]
if(lastSent >= Game.time - limit) {
return
} else {
Expand All @@ -25,18 +31,19 @@ var Notify = function (message, limit) {
}

// Record message in history and send it.
Memory.__notify_history[message] = Game.time
Notify.queueMessage(message)
Memory.__notify_history[queue_message] = Game.time
Notify.queueMessage(message, groups)
return 0
}


Notify.queueMessage = function (message) {
Notify.queueMessage = function (message, groups) {
if(!Memory.__notify) {
Memory.__notify = []
}
Memory.__notify.push({
'message': message,
'groups': groups,
'tick': Game.time
})
}
Expand All @@ -60,5 +67,4 @@ Notify.cleanHistory = function (limit) {
}
}


module.exports = Notify
2 changes: 1 addition & 1 deletion js/notify.d.ts
@@ -1,4 +1,4 @@
declare module "notify" {
function Notify(message: string, limit?: number): void;
function Notify(message: string, limit?: number, groups?: string[]): void;
export = Notify;
}
111 changes: 21 additions & 90 deletions screeps_notify/notify.py
@@ -1,37 +1,22 @@
#!/usr/bin/env python

import requests
from screeps import ScreepsConnection
import screepsapi
import sys
import time
from twilio.rest import TwilioRestClient

import logging
import os
import traceback
import yaml

base_directory = os.path.expanduser('~')
if not os.path.exists(base_directory):
os.makedirs(base_directory)


def getSettings():
if not getSettings.settings:
cwd = os.getcwd()
path = cwd + '/.settings.yaml'
if not os.path.isfile(path):
print 'no settings file found'
sys.exit(-1)
return False
with open(path, 'r') as f:
getSettings.settings = yaml.load(f)
return getSettings.settings
getSettings.settings = False
import services.config as config
import services.messenger as messenger


def getScreepsConnection():
if not getScreepsConnection.sconn:
settings = getSettings()
getScreepsConnection.sconn = ScreepsConnection(
settings = config.getSettings()
getScreepsConnection.sconn = screepsapi.API(
u=settings['screeps_username'],
p=settings['screeps_password'],
ptr=settings['screeps_ptr'])
Expand All @@ -55,75 +40,9 @@ def clearNotifications(tick=0):
sconn.console(javascript_clear)


def sendSMS(notification):
message = 'Screeps: ' + notification['message']
settings = getSettings()

if 'twilio_sid' not in settings:
print('skipping sms due to lack of settings.')
return

smsclient = TwilioRestClient(settings['twilio_sid'],
settings['twilio_token'])
message = smsclient.messages.create(
body=message,
to=settings['sms_to'], # Replace with your phone number
from_=settings['sms_from']) # Replace with your Twilio number
print(message.sid)


def sendHTTP(notification):
settings = getSettings()

if 'http' not in settings:
print('skipping http due to lack of settings.')
return

notification['user'] = settings['screeps_username']
headers = {
'user-agent': 'screeps_notify',
}

if 'api-key' in settings:
headers['x-api-key'] = settings['api-key']

print headers
if 'http_user' in settings:
r = requests.post(settings['http'],
json=notification,
headers=headers,
auth=(settings['http_user'],
settings['http_password']))
else:
r = requests.post(settings['http'],
json=notification,
headers=headers)

print r.text
print r.status_code
return r.status_code == requests.codes.ok


class App():

def __init__(self):
self.stdin_path = '/dev/null'
self.stdout_path = '/dev/null'
# self.stdout_path = base_directory + '/screepsnotify.out'
self.stderr_path = base_directory + '/screepsnotify.err'
self.pidfile_path = base_directory + '/screepsnotify.pid'
self.pidfile_timeout = 5

def run(self):
logging.basicConfig(level=logging.WARN)
logger = logging.getLogger("ScreepsNotify")
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler = logging.FileHandler(base_directory + "/screepsnotify.log")
handler.setFormatter(formatter)
logger.addHandler(handler)

while True:
notifications = getNotifications()
if not notifications or len(notifications) <= 0:
Expand All @@ -134,8 +53,20 @@ def run(self):
for notification in notifications:
if notification['tick'] > limit:
limit = notification['tick']
sendSMS(notification)
sendHTTP(notification)

if 'groups' in notification:
groups = notification['groups']
else:
groups = ['default']

services = config.getServicesFromGroups(groups)
for service in services:
try:
driver = messenger.getMessengerDriver(service)
driver.sendMessage(notification['message'])
except:
traceback.print_exc()

clearNotifications(limit)
time.sleep(5)

Expand Down
Empty file.
27 changes: 27 additions & 0 deletions screeps_notify/services/config.py
@@ -0,0 +1,27 @@
import os
import sys
import yaml

settings = False
cwd = os.getcwd()
path = cwd + '/.settings.yaml'
assert os.path.isfile(path)
with open(path, 'r') as f:
settings = yaml.load(f)

def getSettings():
return settings

def getServicesFromGroups(groups):
ret_services = []
for group in groups:
if group in settings['groups']:
if settings['groups'][group] == 'all':
return list(settings['services'].keys())
for service in settings['groups'][group]:
if service not in ret_services:
ret_services.append(service)
if len(ret_services) < 1:
if 'default' not in groups:
return getServicesFromGroups(['default'])
return ret_services
33 changes: 33 additions & 0 deletions screeps_notify/services/messenger.py
@@ -0,0 +1,33 @@
import importlib
from config import settings
driver_cache = {}
service_cache = {}
assert 'services' in settings
services = settings['services']


def getMessengerDriver(name):
if name in service_cache:
return service_cache[name]
if name not in services:
print('name not in services')
return False
service_settings = services[name]
if 'type' not in service_settings:
print('driver not in service settings')
return False
driver_module = getDriverModule(service_settings['type'])
driver_class = getattr(driver_module, service_settings['type'])
driver = driver_class(service_settings)

service_cache[name] = driver
return driver


# Dynamically import module
def getDriverModule(driver):
if driver in driver_cache:
return driver_cache[driver]
driver = 'services.messengers.%s' % (driver,)
driver_cache[driver] = importlib.import_module(driver, 'screeps_notify')
return driver_cache[driver]
Empty file.
34 changes: 34 additions & 0 deletions screeps_notify/services/messengers/http.py
@@ -0,0 +1,34 @@

import services.config as config
import requests


class http:

def __init__(self, settings):
self.settings = settings

def sendMessage(self, notification):
print('sending message from http')

notification['user'] = config.settings['screeps_username']
headers = {'user-agent': 'screeps_notify'}
if 'api-key' in self.settings:
headers['x-api-key'] = self.settings['api-key']

if 'http_user' in self.settings:
r = requests.post(self.settings['url'],
json=notification,
headers=headers,
auth=(self.settings['http_user'],
self.settings['http_password']))
else:
r = requests.post(self.settings['url'],
json=notification,
headers=headers)

if r.status_code != requests.codes.ok:
raise ValueError(
'http request returned an error %s, the response is:\n%s'
% (r.status_code, r.text))
return r.status_code == requests.codes.ok

0 comments on commit da94864

Please sign in to comment.