mirror of
https://github.com/nikdoof/dropbot.git
synced 2025-12-26 16:19:23 +00:00
Compare commits
41 Commits
0.2.0
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3153da23c | ||
| 49e55b22f4 | |||
| 9da66831fa | |||
| 19c2ccdfde | |||
|
|
b4495d346a | ||
|
|
4d16f31209 | ||
|
|
84794533d8 | ||
|
|
cdfefdcffd | ||
|
|
c3ffc2af0a | ||
|
|
f65ff4c6aa | ||
| 7b88760d0a | |||
| 56f2e40d4d | |||
| 8e3996054d | |||
| 8b6c3bf1eb | |||
| f2685a05d2 | |||
| 6545a8fad3 | |||
| 5d296b1aab | |||
| 971abfa714 | |||
| 63e42d6df5 | |||
|
|
b026a6e37c | ||
|
|
af8c1b474d | ||
|
|
a3c90d2487 | ||
|
|
90a04ee857 | ||
|
|
bf9f879b32 | ||
|
|
39eb3b9b0e | ||
|
|
034fb3a2dd | ||
|
|
35da7264c7 | ||
|
|
0d563c479b | ||
|
|
c7a5377740 | ||
|
|
3d18e3d127 | ||
|
|
4b24b95904 | ||
|
|
5c6030cd74 | ||
| fd5b08227f | |||
| feb70dccf2 | |||
| 7d3d933189 | |||
| de32857247 | |||
| 8b642165c5 | |||
| ab8857aea6 | |||
| 58cdc22acb | |||
| 5fd225aa55 | |||
| 41f71e2086 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -13,6 +13,7 @@ env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
.eggs/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
@@ -40,6 +41,7 @@ htmlcov/
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
_trial_temp/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
@@ -62,3 +64,8 @@ eve.db
|
||||
|
||||
# Vagrant
|
||||
.vagrant/
|
||||
|
||||
# Cruft
|
||||
*.json
|
||||
sde.sqlite
|
||||
sde.sqlite.bz2
|
||||
|
||||
10
README.md
10
README.md
@@ -39,6 +39,7 @@ The configuration is passed by using environment variables.
|
||||
* ```DROPBOT_REDIS_URL``` - 12 factor style URL of the Redis server to use (defaults to redis://localhost:6379/0)
|
||||
* ```DROPBOT_CMD_PREFIX``` - Prefix of MUC channel commands (defaults to !)
|
||||
* ```DROPBOT_KOS_URL``` - URL of the CVA KOS API service (defaults to http://kos.cva-eve.org/api/)
|
||||
* ```DROPBOT_MARKET_SYSTEMS``` - A comma seperated list of systems to be used for the best price checker (defaults to Jita, Amarr, Rens, Dodixie, Hek)
|
||||
* ```DROPBOT_KILL_CORPS``` - List of Corp IDs to track for kills
|
||||
* ```DROPBOT_KILLS_DISABLED``` - Disables the streaming of zKillboard kills to the channels (default to 0)
|
||||
* ```DROPBOT_OFFICE_API_KEYID``` - API KeyID to use for the nearest office finder.
|
||||
@@ -49,12 +50,15 @@ Updating the SDE data
|
||||
|
||||
To update the SDE data in the bot, use the ```gen_reference_data.py``` with a copy of the Sqlite conversion of the SDE, this will produce three json files that need to be copied to the data directory witthin the ```dropbot``` package.
|
||||
|
||||
$ python gen_reference_data.py eve.db
|
||||
The SDE conversion is usually available here: https://www.fuzzwork.co.uk/dump/
|
||||
|
||||
$ wget https://www.fuzzwork.co.uk/dump/sqlite-latest.sqlite.bz2
|
||||
$ python gen_reference_data.py sqlite-latest.sqlite.bz2
|
||||
Importing Types...
|
||||
Importing Stations...
|
||||
Importing Map...
|
||||
$ ls -1
|
||||
$ ls *.json
|
||||
map.json
|
||||
stations.json
|
||||
types.json
|
||||
|
||||
$ cp -i map.json stations.json types.json dropbot/data/
|
||||
|
||||
@@ -20,14 +20,6 @@ from dropbot.stomp_listener import ZKillboardStompListener
|
||||
|
||||
urlparse.uses_netloc.append("redis")
|
||||
|
||||
market_systems = [
|
||||
('Jita', 30000142),
|
||||
('Amarr', 30002187),
|
||||
('Rens', 30002510),
|
||||
('Dodixie', 30002659),
|
||||
('U-HVIX', 30000575),
|
||||
]
|
||||
|
||||
zkillboard_regex = re.compile(r'http(s|):\/\/(?P<host>.*)\/kill\/(?P<killID>\d+)\/')
|
||||
|
||||
|
||||
@@ -48,7 +40,8 @@ class DropBot(ClientXMPP):
|
||||
self.kills_muted = False
|
||||
self.office_api_key_keyid = kwargs.pop('office_api_keyid', None)
|
||||
self.office_api_key_vcode = kwargs.pop('office_api_vcode', None)
|
||||
|
||||
self.market_systems = kwargs.pop('market_systems', ['Jita', 'Amarr', 'Rens', 'Dodixie', 'Hek'])
|
||||
|
||||
if 'redis_url' in kwargs:
|
||||
self.redis_pool = ConnectionPool.from_url(kwargs.pop('redis_url', 'redis://localhost:6379/0'))
|
||||
self.redis = Redis(connection_pool=self.redis_pool)
|
||||
@@ -181,7 +174,7 @@ class DropBot(ClientXMPP):
|
||||
if item.strip() == '':
|
||||
return 'Usage: !price <item>'
|
||||
if item.lower() == 'plex':
|
||||
item = '30 Day'
|
||||
return (u"29668", u"30 Day Pilot's License Extension (PLEX)")
|
||||
types = dict([(i, v) for i, v in self.types.iteritems() if item.lower() in v.lower()])
|
||||
if len(types) == 0:
|
||||
return "No items named {} found".format(item)
|
||||
@@ -289,7 +282,6 @@ class DropBot(ClientXMPP):
|
||||
else:
|
||||
return 'Unknown command'
|
||||
|
||||
|
||||
def cmd_bestprice(self, args, msg):
|
||||
"""Returns the best price for an item out of the current known market hub systems"""
|
||||
item = ' '.join(args)
|
||||
@@ -303,7 +295,10 @@ class DropBot(ClientXMPP):
|
||||
sell_sys = None
|
||||
buy_sys = None
|
||||
|
||||
for name, sys_id in market_systems:
|
||||
for name in self.market_systems:
|
||||
sys_id = self.map.get_system_id(name)
|
||||
if not sys_id:
|
||||
continue
|
||||
sell, buy = self._get_evecentral_price(type_id, sys_id)
|
||||
if (sell < min_sell or min_sell == 0) and sell > 0:
|
||||
min_sell = sell
|
||||
@@ -350,12 +345,12 @@ class DropBot(ClientXMPP):
|
||||
return self.cmd_price(['Rens'] + args, msg)
|
||||
|
||||
def cmd_dodixie(self, args, msg):
|
||||
"""Returns the price of a item in Dodixie"""
|
||||
"""Returns the price of a item in Dodixie"""
|
||||
return self.cmd_price(['Dodixie'] + args, msg)
|
||||
|
||||
def cmd_uh(self, args, msg):
|
||||
"""Returns the price of a item in U-HVIX"""
|
||||
return self.cmd_price(['U-HVIX'] + args, msg)
|
||||
|
||||
def cmd_hek(self, args, msg):
|
||||
"""Returns the price of a item in Hek"""
|
||||
return self.cmd_price(['Hek'] + args, msg)
|
||||
|
||||
def cmd_r(self, args, msg):
|
||||
return self.cmd_redditimg(args, msg)
|
||||
@@ -702,7 +697,6 @@ class DropBot(ClientXMPP):
|
||||
self.schedule('unmute', 30 * 60, unmute, [self])
|
||||
return 'Killmails muted, posting will resume automatically in 30 minutes'
|
||||
|
||||
|
||||
def cmd_nearestoffice(self, args, msg):
|
||||
if len(args) != 1:
|
||||
return '!nearestoffice <system>'
|
||||
|
||||
@@ -33,7 +33,7 @@ def main():
|
||||
# Parse the environment for config
|
||||
config = dict([(k[8:].lower(), v) for k, v in os.environ.items() if 'DROPBOT_' in k])
|
||||
# Split out array type configs
|
||||
for key in ['rooms', 'admins', 'kill_corps']:
|
||||
for key in ['rooms', 'admins', 'kill_corps', 'market_systems']:
|
||||
if key in config:
|
||||
config[key] = [x.strip() for x in config[key].split(',')]
|
||||
elif opts.config.lower().startswith('http'):
|
||||
@@ -65,4 +65,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -52,21 +52,35 @@ isotope_usage = {
|
||||
'blackops': 450,
|
||||
}
|
||||
|
||||
fatiuge_bonus = {
|
||||
'blackops': (.5, .5),
|
||||
'covertops': (0, .5),
|
||||
'forcerecon': (0, .5),
|
||||
'blockaderunner': (.9, .95),
|
||||
'industrial': (.9, 0),
|
||||
'freighter': (.9, 0),
|
||||
'jumpfreighter': (.9, 0),
|
||||
'deepspacetransport': (.9, 0),
|
||||
}
|
||||
|
||||
|
||||
EVE_LY = 9460000000000000 # EVE's definition of a ly in KM
|
||||
|
||||
JDC_BONUS = 0.20
|
||||
|
||||
|
||||
def calc_distance(sys1, sys2):
|
||||
"""Calculate the distance in light years between two sets of 3d coordinates"""
|
||||
return math.sqrt(sum((a - b)**2 for a, b in zip(sys1, sys2))) / EVE_LY
|
||||
|
||||
|
||||
def hull_to_range(hull, jdc_skill):
|
||||
"""Returns the jump range of a provided ship hull and Jump Drive Calibration skill"""
|
||||
if hull.lower() not in hull_classes:
|
||||
raise ValueError('Unknown hull class {}'.format(hull))
|
||||
return ship_class_to_range(hull_classes[hull.lower()], jdc_skill)
|
||||
|
||||
|
||||
def ship_class_to_range(ship_class, jdc_skill):
|
||||
"""Returns the jump range of a provided ship class and Jump Drive Calibration skill"""
|
||||
if ship_class.lower() not in base_range:
|
||||
@@ -84,9 +98,10 @@ class Map(networkx.Graph):
|
||||
def from_sde(self, db_conn):
|
||||
"""Load map data from a EVE SDE Sqlite DB"""
|
||||
for id, name, region_name, x, y, z, security in db_conn.execute("""
|
||||
SELECT solarSystemID, solarSystemName, regionName, mapSolarSystems.x, mapSolarSystems.y, mapSolarSystems.z, mapSolarSystems.security
|
||||
FROM mapSolarSystems
|
||||
INNER JOIN mapRegions ON mapSolarSystems.regionID = mapRegions.regionID"""):
|
||||
SELECT solarSystemID, solarSystemName, regionName, mapSolarSystems.x, mapSolarSystems.y, mapSolarSystems.z, mapSolarSystems.security
|
||||
FROM mapSolarSystems
|
||||
INNER JOIN mapRegions ON mapSolarSystems.regionID = mapRegions.regionID
|
||||
"""):
|
||||
self.add_node(id, system_id=id, name=name, region=region_name, coords=(x, y, z), security=security)
|
||||
for from_id, to_id in db_conn.execute("SELECT fromSolarSystemID, toSolarSystemID FROM mapSolarSystemJumps"):
|
||||
self.add_edge(from_id, to_id, weight=1, link_type='gate')
|
||||
@@ -135,18 +150,15 @@ class Map(networkx.Graph):
|
||||
|
||||
def route_gate(self, source, destination, filter=None):
|
||||
"""Route between two systems using gates (fastest)"""
|
||||
|
||||
# TODO: add EVE routing options (highsec/lowsec/fastest)
|
||||
|
||||
g = networkx.Graph(data=[(u, v) for u, v, d in self.edges_iter(data=True) if d['link_type'] == 'gate' or d['link_type'] == 'bridge'])
|
||||
return networkx.astar_path(self, source, destination)
|
||||
|
||||
def _route_jump_fast(self, source, destination, range=None, hull=None, ship_class=None, station_only=False, avoid_systems=[]):
|
||||
"""A fast but error prone route calculation between two systems using jumps"""
|
||||
print source, destination
|
||||
route = [source]
|
||||
current_system = source
|
||||
while not destination in route:
|
||||
while destination not in route:
|
||||
next_distance = None
|
||||
next_system = None
|
||||
# Iterate through jump neighbour systems to find the best candidate
|
||||
@@ -160,7 +172,6 @@ class Map(networkx.Graph):
|
||||
if system['system_id'] == destination:
|
||||
route.append(destination)
|
||||
return route
|
||||
|
||||
# Use heuristics to identify the best candidate (one that gets us closest to the target)
|
||||
distance_to_target = self.system_distance(system['system_id'], destination)
|
||||
if distance_to_target < next_distance or not next_distance:
|
||||
@@ -171,14 +182,14 @@ class Map(networkx.Graph):
|
||||
|
||||
def route_jump(self, source, destination, range=None, hull=None, ship_class=None, station_only=False, avoid_systems=[]):
|
||||
"""Calculate a jump route between two systems"""
|
||||
closed = set()
|
||||
open = set([source])
|
||||
closed_set = set()
|
||||
open_set = set([source])
|
||||
route = {}
|
||||
g_score = {source: 0}
|
||||
f_score = {source: g_score[source] + self.system_distance(source, destination)}
|
||||
|
||||
while len(open):
|
||||
current = min([x for x in f_score.items() if x[0] in open], key=lambda x: x[1])[0]
|
||||
while len(open_set):
|
||||
current = min([x for x in f_score.items() if x[0] in open_set], key=lambda x: x[1])[0]
|
||||
if current == destination:
|
||||
|
||||
def build_path(route, current):
|
||||
@@ -186,26 +197,26 @@ class Map(networkx.Graph):
|
||||
p = build_path(route, route[current])
|
||||
p.append(current)
|
||||
return p
|
||||
return [current]
|
||||
return [current]
|
||||
|
||||
return build_path(route, destination)
|
||||
open.remove(current)
|
||||
closed.add(current)
|
||||
open_set.remove(current)
|
||||
closed_set.add(current)
|
||||
for neighbor, distance in self.neighbors_jump(current, range, hull, ship_class):
|
||||
neighbor_id = neighbor['system_id']
|
||||
if neighbor_id in closed or \
|
||||
if neighbor_id in closed_set or \
|
||||
neighbor['security'] >= 0.45 or \
|
||||
(station_only and not neighbor['station']) or \
|
||||
neighbor_id in avoid_systems:
|
||||
continue
|
||||
|
||||
score = g_score[current] + self.system_distance(current, neighbor_id)
|
||||
if neighbor_id not in open or score < g_score[neighbor_id]:
|
||||
if neighbor_id not in open_set or score < g_score[neighbor_id]:
|
||||
route[neighbor_id] = current
|
||||
g_score[neighbor_id] = score
|
||||
f_score[neighbor_id] = score + self.system_distance(neighbor_id, destination)
|
||||
if neighbor_id not in open:
|
||||
open.add(neighbor_id)
|
||||
if neighbor_id not in open_set:
|
||||
open_set.add(neighbor_id)
|
||||
|
||||
def route_jump_distance(self, route):
|
||||
"""Calculate the total ly distance of a route"""
|
||||
@@ -230,10 +241,38 @@ class Map(networkx.Graph):
|
||||
multi = 1 - (.1 * jfc_skill)
|
||||
if ship_class == 'jumpfreighter':
|
||||
multi = multi * (1 - (.1 * jf_skill))
|
||||
base = isotope_usage[ship_class] * multi
|
||||
ly = self.route_jump_distance(route)
|
||||
base = isotope_usage[ship_class] * multi
|
||||
ly = self.route_jump_distance(route)
|
||||
return round(ly * base, 0)
|
||||
|
||||
def jump_fatigue(self, fatigue, source, destination, bonus=0, ship_class=None, jump_type='standard'):
|
||||
"""Calculate the jump fatigue gained by jumping between two systems"""
|
||||
if bonus == 0 and ship_class:
|
||||
standard = covert = 0
|
||||
if ship_class and ship_class in fatiuge_bonus:
|
||||
standard, covert = fatiuge_bonus[ship_class]
|
||||
if jump_type == 'standard':
|
||||
bonus = standard
|
||||
else:
|
||||
bonus = covert
|
||||
distance = self.system_distance(source, destination)
|
||||
cooldown = max(fatigue / 10, 1 + (distance * (1-bonus)))
|
||||
new_fatigue = min(60 * 24 * 30, max(fatigue, 10) * (1 + (distance * (1 - bonus))))
|
||||
return round(cooldown, 2), round(new_fatigue, 2)
|
||||
|
||||
def route_jump_fatigue(self, route, fatigue, bonus=0, ship_class=None, jump_type='standard'):
|
||||
"""Calculate the jump fatigue for the specified route"""
|
||||
results = []
|
||||
source = route.pop(0)
|
||||
for target in route:
|
||||
cooldown, new_fatigue = self.jump_fatigue(fatigue, source, target, bonus, ship_class, jump_type)
|
||||
results.append({
|
||||
'source': source, 'target': target, 'cooldown': cooldown, 'fatigue': new_fatigue,
|
||||
})
|
||||
source = target
|
||||
fatigue = new_fatigue
|
||||
return results
|
||||
|
||||
def neighbors_gate(self, system_id):
|
||||
"""List systems that are connected to a system by gates"""
|
||||
return self.neighbors(system_id)
|
||||
@@ -260,9 +299,8 @@ class Map(networkx.Graph):
|
||||
if destination_data['coords'][0] > range_x[0] or destination_data['coords'][0] < range_x[1] or \
|
||||
destination_data['coords'][1] > range_y[0] or destination_data['coords'][1] < range_y[1] or \
|
||||
destination_data['coords'][2] > range_z[0] or destination_data['coords'][2] < range_z[1]:
|
||||
continue
|
||||
continue
|
||||
distance = calc_distance(source['coords'], destination_data['coords'])
|
||||
if distance <= range and destination_id != system_id:
|
||||
destinations.append((destination_data, distance))
|
||||
return destinations
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import logging
|
||||
|
||||
urlparse.uses_netloc.append('tcp')
|
||||
|
||||
|
||||
class ZKillboardStompListener(stomp.listener.ConnectionListener):
|
||||
|
||||
def __init__(self, bot):
|
||||
@@ -53,4 +54,4 @@ class ZKillboardStompListener(stomp.listener.ConnectionListener):
|
||||
self.conn.set_listener('', self)
|
||||
self.conn.start()
|
||||
self.conn.connect('guest', 'guest')
|
||||
self.conn.subscribe('/topic/kills', id='dropbot')
|
||||
self.conn.subscribe('/topic/kills', id='dropbot')
|
||||
|
||||
@@ -2,6 +2,36 @@ from hashlib import sha1
|
||||
import zlib
|
||||
import redis
|
||||
import logging
|
||||
import math
|
||||
|
||||
|
||||
def decimal_minutes_to_hms(minutes):
|
||||
"""Converts a value of decimal minutes into a hms format"""
|
||||
if not isinstance(minutes, (int, float, long)):
|
||||
if isinstance(minutes, basestring):
|
||||
try:
|
||||
minutes = float(minutes)
|
||||
except ValueError:
|
||||
raise ValueError('minutes is not a valid number')
|
||||
else:
|
||||
raise ValueError('minutes is not a valid number')
|
||||
|
||||
# If we have a negative number, invert
|
||||
if minutes < 0:
|
||||
minutes = -1 * minutes
|
||||
|
||||
out_secs = round(60 * (minutes % 1))
|
||||
out_minutes = math.floor(minutes) % 60
|
||||
out_hours = math.floor(math.floor(minutes) / 60)
|
||||
|
||||
output = ''
|
||||
if out_hours > 0:
|
||||
output += '{}h '.format(int(out_hours))
|
||||
if out_minutes > 0:
|
||||
output += '{}m '.format(int(out_minutes))
|
||||
if out_secs > 0:
|
||||
output += '{}s '.format(int(out_secs))
|
||||
return output.strip()
|
||||
|
||||
|
||||
class EVEAPIRedisCache(object):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
sleekxmpp==1.3.1
|
||||
eveapi==1.2.6
|
||||
redis==2.10.2
|
||||
redis==4.4.4
|
||||
requests==2.3.0
|
||||
humanize==0.5
|
||||
dnspython==1.11.1
|
||||
|
||||
24
sde_update.sh
Executable file
24
sde_update.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
SDE_URL="https://www.fuzzwork.co.uk/dump/sqlite-latest.sqlite.bz2"
|
||||
|
||||
echo "Downloading SDE from ${SDE_URL}"
|
||||
curl -o sde.sqlite.bz2 "${SDE_URL}" && bunzip2 -f sde.sqlite.bz2
|
||||
if [ -e sde.sqlite ]; then
|
||||
echo "Generating reference data"
|
||||
/usr/bin/env python gen_reference_data.py sde.sqlite
|
||||
cp *.json dropbot/data/
|
||||
|
||||
echo "Running unit tests"
|
||||
/usr/bin/env python setup.py test > /dev/null 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
git status
|
||||
echo "SDE data updated successfully"
|
||||
else
|
||||
echo "Unit tests failed, please investigate"
|
||||
fi
|
||||
else
|
||||
echo "Error downloading SDE"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,3 +1,6 @@
|
||||
import os
|
||||
import unittest
|
||||
import mock
|
||||
from unittest import TestCase
|
||||
from dropbot.bot import DropBot
|
||||
|
||||
@@ -7,6 +10,11 @@ class DropBotTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.bot = DropBot('test@test.com', 'testpassword')
|
||||
|
||||
def call_command(self, command, args=[]):
|
||||
"""Fakes a call to a bot command"""
|
||||
msg = {'type': 'groupchat'}
|
||||
return self.bot.call_command(command, args, msg)
|
||||
|
||||
def test_simple_bot(self):
|
||||
self.assertIsNotNone(self.bot)
|
||||
|
||||
@@ -18,6 +26,141 @@ class DropBotTestCase(TestCase):
|
||||
self.assertEqual(self.bot._system_picker('GE-'), 'Did you mean: GE-94X, GE-8JV?')
|
||||
self.assertEqual(self.bot._system_picker('asdasd'), 'No systems found matching asdasd')
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_get_evecentral_price(self):
|
||||
self.assertIs(self.bot._get_evecentral_price(1,1), None)
|
||||
self.assertIs(type(self.bot._get_evecentral_price(22430, 30000142)), tuple)
|
||||
self.assertIs(type(self.bot._get_evecentral_price(22430, 30000142)), tuple)
|
||||
|
||||
def test_cmd_help(self):
|
||||
res = self.call_command('help')
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_bestprice(self):
|
||||
res = self.call_command('bestprice', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_price(self):
|
||||
res = self.call_command('price', args=['jita', 'rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_jita(self):
|
||||
res = self.call_command('jita', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_amarr(self):
|
||||
res = self.call_command('amarr', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_rens(self):
|
||||
res = self.call_command('rens', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_dodixie(self):
|
||||
res = self.call_command('dodixie', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_uh(self):
|
||||
res = self.call_command('uh', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_hek(self):
|
||||
res = self.call_command('hek', ['rifter'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_r(self):
|
||||
pass
|
||||
|
||||
def test_cmd_redditimg(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_kos(self):
|
||||
res = self.call_command('kos', ['Palkark'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_range(self):
|
||||
res = self.call_command('range', ['U-HVIX'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_route(self):
|
||||
res = self.call_command('route', ['Jita', 'Amarr'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_addjb(self):
|
||||
res = self.call_command('addjb', ['Jita', 'Amarr'])
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
self.assertEqual(res[0], 'Done')
|
||||
|
||||
def test_cmd_listjbs(self):
|
||||
res = self.call_command('listjbs')
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsNone(res[0], None)
|
||||
|
||||
self.call_command('addjb', ['Jita', 'Amarr'])
|
||||
res = self.call_command('listjbs')
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_mapstats(self):
|
||||
res = self.call_command('mapstats')
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
|
||||
def test_cmd_hit(self):
|
||||
pass
|
||||
|
||||
def test_cmd_jump(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_id(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_kill(self):
|
||||
pass
|
||||
|
||||
def test_cmd_mute(self):
|
||||
self.assertEqual(self.bot.kills_muted, False)
|
||||
res = self.call_command('mute')
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
self.assertEqual(res[0], 'Killmails muted, posting will resume automatically in 30 minutes')
|
||||
self.assertEqual(self.bot.kills_muted, True)
|
||||
|
||||
@unittest.skipIf(os.environ.get('NO_NETWORK', '0') == '1', 'No networking, skipping test')
|
||||
def test_cmd_nearestoffice(self):
|
||||
pass
|
||||
|
||||
def test_cmd_rageping(self):
|
||||
pass
|
||||
|
||||
def test_jackdaw(self):
|
||||
"""
|
||||
The items in the Carnyx release can be found.
|
||||
"""
|
||||
self.assertEqual(self.bot._item_picker("Jackdaw"), (u'34828', u'Jackdaw'))
|
||||
|
||||
def test_carnyx_plex(self):
|
||||
self.assertEqual(self.bot._item_picker("plex"), (u"29668", "30 Day Pilot's License Extension (PLEX)"))
|
||||
|
||||
@@ -8,33 +8,39 @@ class MapTestCase(TestCase):
|
||||
self.map = Map.from_json(pkgutil.get_data('dropbot', 'data/map.json'))
|
||||
|
||||
def test_load_from_package_data(self):
|
||||
"""Check the package data can be correctly loaded into the map"""
|
||||
m = Map.from_json(pkgutil.get_data('dropbot', 'data/map.json'))
|
||||
self.assertIsNotNone(m)
|
||||
|
||||
def test_get_system_name(self):
|
||||
"""Test looking up system names from IDs"""
|
||||
self.assertEquals(self.map.get_system_name(123), None)
|
||||
self.assertEqual(self.map.get_system_name(30000142), 'Jita')
|
||||
|
||||
def test_get_system_id(self):
|
||||
"""Test looking up system ID by name"""
|
||||
self.assertEqual(self.map.get_system_id('Llamatron'), None)
|
||||
self.assertEqual(self.map.get_system_id('Jita'), 30000142)
|
||||
|
||||
def test_get_systems(self):
|
||||
"""Check partial matching of system names works correctly"""
|
||||
self.assertEquals(len(self.map.get_systems('Jita')), 1)
|
||||
self.assertEquals(len(self.map.get_systems('Ji')), 7)
|
||||
self.assertEquals(len(self.map.get_systems('J')), 2760)
|
||||
self.assertEquals(len(self.map.get_systems('J')), 2765)
|
||||
self.assertEquals(len(self.map.get_systems('123435345345')), 0)
|
||||
self.assertEquals(len(self.map.get_systems('jita')), 1)
|
||||
self.assertEquals(len(self.map.get_systems('JITA')), 1)
|
||||
self.assertEquals(len(self.map.get_systems('JiTa')), 1)
|
||||
|
||||
def test_system_distance(self):
|
||||
"""Test the distance calculator"""
|
||||
self.assertEqual(self.map.system_distance(30000142, 30000144), 2.10268108033618)
|
||||
self.assertEqual(self.map.system_distance(30000142, 30000222), 9.334275248404591)
|
||||
self.assertEqual(self.map.system_distance(30000142, 30000536), 39.15289747780095)
|
||||
self.assertRaises(Exception, self.map.system_distance, (1, 2))
|
||||
|
||||
def test_route_gate(self):
|
||||
"""Test the gate routing system"""
|
||||
r = self.map.route_gate(30001161, 30001198)
|
||||
self.assertEqual(len(r), 9)
|
||||
self.assertListEqual(r, [30001161, 30001158, 30001160, 30001154, 30001157, 30001155, 30001156, 30001162, 30001198])
|
||||
@@ -48,6 +54,63 @@ class MapTestCase(TestCase):
|
||||
def test_route_jump_isotopes(self):
|
||||
pass
|
||||
|
||||
def test_jump_fatigue_standard(self):
|
||||
"""Test jump fatigue calculator, 4.119ly jump, no bonus"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('V-IUEL')
|
||||
cooldown, new_fatigue = self.map.jump_fatigue(0, sys1, sys2)
|
||||
self.assertEqual(cooldown, 5.12)
|
||||
self.assertEqual(new_fatigue, 51.19)
|
||||
|
||||
def test_jump_fatigue_covertops(self):
|
||||
"""Test jump fatigue calculator, 4.119ly jump, covertops being bridged by titan"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('V-IUEL')
|
||||
cooldown, new_fatigue = self.map.jump_fatigue(0, sys1, sys2, ship_class='covertops')
|
||||
self.assertEqual(cooldown, 5.12)
|
||||
self.assertEqual(new_fatigue, 51.19)
|
||||
|
||||
def test_jump_fatigue_covertops_covertcyno(self):
|
||||
"""Test jump fatigue calculator, 4.119ly jump, covertops being bridged by blops"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('V-IUEL')
|
||||
cooldown, new_fatigue = self.map.jump_fatigue(0, sys1, sys2, ship_class='covertops', jump_type='covert')
|
||||
self.assertEqual(cooldown, 3.06)
|
||||
self.assertEqual(new_fatigue, 30.59)
|
||||
|
||||
def test_route_jump_fatigue_covertops_covertcyno(self):
|
||||
"""Test jump fatigue calculator, 4.119ly jump, covertops being bridged by blops"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('V-IUEL')
|
||||
res = self.map.route_jump_fatigue([sys1, sys2], 0, ship_class='covertops', jump_type='covert')
|
||||
self.assertListEqual(res, [
|
||||
{'source': sys1, 'target': sys2, 'cooldown': 3.06, 'fatigue': 30.59,}
|
||||
])
|
||||
|
||||
def test_route_jump_fatigue_covertops_covertcyno_two_jumps(self):
|
||||
"""Test route jump fatigue calculator, covertops being bridged by blops for two jumps"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('RMOC-W')
|
||||
sys3 = self.map.get_system_id('Podion')
|
||||
res = self.map.route_jump_fatigue([sys1, sys2, sys3], 0, ship_class='covertops', jump_type='covert')
|
||||
self.assertListEqual(res, [
|
||||
{'source': sys1, 'target': sys2, 'cooldown': 4.77, 'fatigue': 47.72, },
|
||||
{'source': sys2, 'target': sys3, 'cooldown': 4.77, 'fatigue': 222.91, },
|
||||
])
|
||||
|
||||
def test_route_jump_fatigue_covertops_covertcyno_three_jumps(self):
|
||||
"""Test route jump fatigue calculator, covertops being bridged by blops for three jumps"""
|
||||
sys1 = self.map.get_system_id('U-HVIX')
|
||||
sys2 = self.map.get_system_id('RMOC-W')
|
||||
sys3 = self.map.get_system_id('Podion')
|
||||
sys4 = self.map.get_system_id('Hothomouh')
|
||||
res = self.map.route_jump_fatigue([sys1, sys2, sys3, sys4], 0, ship_class='covertops', jump_type='covert')
|
||||
self.assertListEqual(res, [
|
||||
{'source': sys1, 'target': sys2, 'cooldown': 4.77, 'fatigue': 47.72, },
|
||||
{'source': sys2, 'target': sys3, 'cooldown': 4.77, 'fatigue': 222.91, },
|
||||
{'source': sys3, 'target': sys4, 'cooldown': 22.29, 'fatigue': 786.39, },
|
||||
])
|
||||
|
||||
def test_neighbors_gate(self):
|
||||
pass
|
||||
|
||||
@@ -55,7 +118,7 @@ class MapTestCase(TestCase):
|
||||
pass
|
||||
|
||||
def test_jump_bridge_addition(self):
|
||||
# HED-GP to GE-8
|
||||
"""Test addition of a jump bridge"""
|
||||
self.assertGreater(len(self.map.route_gate(30001161, 30001198)), 2)
|
||||
self.map.add_jumpbridge(30001161, 30001198)
|
||||
r = self.map.route_gate(30001161, 30001198)
|
||||
@@ -106,3 +169,57 @@ class MapTestCase(TestCase):
|
||||
The maximum jump range of a bloops is 8LY.
|
||||
"""
|
||||
self.assertEquals(ship_class_to_range('blackops', 5), 8)
|
||||
|
||||
def test_jump_distance_skills_titan(self):
|
||||
"""
|
||||
Test the correct range for titans for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('titan', skill), jump_range)
|
||||
|
||||
def test_jump_distance_skills_supercarrier(self):
|
||||
"""
|
||||
Test the correct range for titans for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('supercarrier', skill), jump_range)
|
||||
|
||||
def test_jump_distance_skills_carrier(self):
|
||||
"""
|
||||
Test the correct range for supercarriers for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('carrier', skill), jump_range)
|
||||
|
||||
def test_jump_distance_skills_dreadnought(self):
|
||||
"""
|
||||
Test the correct range for dreadnoughts for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('dreadnought', skill), jump_range)
|
||||
|
||||
def test_jump_distance_skills_industrial(self):
|
||||
"""
|
||||
Test the correct range for industrials for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('industrial', skill), jump_range)
|
||||
|
||||
def test_jump_distance_skills_jumpfreighter(self):
|
||||
"""
|
||||
Test the correct range for jump freighters for each JDC skill level
|
||||
"""
|
||||
ship_ranges = [5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
|
||||
for skill in range(0, 6):
|
||||
jump_range = ship_ranges[skill]
|
||||
self.assertEquals(ship_class_to_range('jumpfreighter', skill), jump_range)
|
||||
|
||||
60
tests/test_utils.py
Normal file
60
tests/test_utils.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import mock
|
||||
from unittest import TestCase
|
||||
from dropbot.utils import decimal_minutes_to_hms
|
||||
|
||||
|
||||
class DecimalToHMSTest(TestCase):
|
||||
"""
|
||||
Tests the decimal_minutes_to_hms function
|
||||
"""
|
||||
|
||||
def test_seconds_only(self):
|
||||
"""Check seconds are calculated correctly"""
|
||||
self.assertEqual(decimal_minutes_to_hms(0.5), '30s')
|
||||
self.assertEqual(decimal_minutes_to_hms(0.75), '45s')
|
||||
self.assertEqual(decimal_minutes_to_hms(0.25), '15s')
|
||||
self.assertEqual(decimal_minutes_to_hms(0.56), '34s')
|
||||
self.assertEqual(decimal_minutes_to_hms(0.57), '34s')
|
||||
self.assertEqual(decimal_minutes_to_hms(0.58), '35s')
|
||||
|
||||
def test_full_minutes(self):
|
||||
"""Check minutes are correclty output"""
|
||||
self.assertEqual(decimal_minutes_to_hms(1), '1m')
|
||||
self.assertEqual(decimal_minutes_to_hms(10), '10m')
|
||||
self.assertEqual(decimal_minutes_to_hms(30), '30m')
|
||||
|
||||
def test_minutes_and_seconds(self):
|
||||
"""Check minutes and seconds are correctly output"""
|
||||
self.assertEqual(decimal_minutes_to_hms(1.5), '1m 30s')
|
||||
self.assertEqual(decimal_minutes_to_hms(59.5), '59m 30s')
|
||||
self.assertEqual(decimal_minutes_to_hms(5.25), '5m 15s')
|
||||
|
||||
def test_full_hours(self):
|
||||
"""Check hours are correctly output"""
|
||||
self.assertEqual(decimal_minutes_to_hms(60), '1h')
|
||||
self.assertEqual(decimal_minutes_to_hms(120), '2h')
|
||||
self.assertEqual(decimal_minutes_to_hms(1440), '24h')
|
||||
|
||||
def test_hour_minutes_and_seconds(self):
|
||||
"""Check HMS are correctly output in the correct situations"""
|
||||
self.assertEqual(decimal_minutes_to_hms(61.5), '1h 1m 30s')
|
||||
|
||||
def test_partial_seconds(self):
|
||||
"""Check that partial seconds are rounded to the nearest second"""
|
||||
self.assertEqual(decimal_minutes_to_hms(4.56), '4m 34s')
|
||||
self.assertEqual(decimal_minutes_to_hms(4.57), '4m 34s')
|
||||
self.assertEqual(decimal_minutes_to_hms(4.58), '4m 35s')
|
||||
|
||||
def test_large_numbers(self):
|
||||
self.assertEqual(decimal_minutes_to_hms(300000000000000), '5000000000000h')
|
||||
self.assertEqual(decimal_minutes_to_hms(3000023423234.4), '50000390387h 14m 24s')
|
||||
|
||||
def test_negative_numbers(self):
|
||||
self.assertEqual(decimal_minutes_to_hms(-1), '1m')
|
||||
self.assertEqual(decimal_minutes_to_hms(-1.2), '1m 12s')
|
||||
|
||||
def test_invalid_input(self):
|
||||
with self.assertRaises(ValueError):
|
||||
decimal_minutes_to_hms('dsd')
|
||||
with self.assertRaises(ValueError):
|
||||
decimal_minutes_to_hms(mock.Mock())
|
||||
Reference in New Issue
Block a user