"
84 |
85 | params = {
86 | "key": api_key,
87 | "fields": "items(id(videoId))",
88 | "part": "snippet",
89 | "type": "video",
90 | "maxResults": "1",
91 | "q": inp,
92 | }
93 |
94 | j = http.get_json(SEARCH_API_URL, **params)
95 |
96 | if "error" in j:
97 | return "error while performing the search"
98 |
99 | results = j.get("items")
100 |
101 | if not results:
102 | return "no results found"
103 |
104 | vid_id = j["items"][0]["id"]["videoId"]
105 |
106 | return get_video_description(vid_id, api_key) + " - " + VIDEO_URL % vid_id
107 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | lxml
2 | future
3 |
--------------------------------------------------------------------------------
/test/__main__.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | from unittest import TestSuite, TestLoader, TextTestRunner
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | # Because the project is structured differently than
7 | # any tooling expects, we need to modify the python
8 | # path during runtime (or before) to get it to
9 | # properly import plugins and other code correctly.
10 | project_root_directory = path.dirname(path.dirname(__file__))
11 |
12 | sys.path.append(path.join(project_root_directory, "plugins"))
13 | sys.path.append(path.join(project_root_directory))
14 |
15 | discovered_tests = TestLoader().discover(path.dirname(__file__))
16 | run_result = TextTestRunner().run(discovered_tests)
17 |
18 | if not run_result.wasSuccessful():
19 | sys.exit(1)
20 |
--------------------------------------------------------------------------------
/test/helpers.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import json
3 | import inspect
4 |
5 |
6 | def get_fixture_file(testcase, file):
7 | filename = inspect.getfile(testcase.__class__)
8 |
9 | test_name, _ = path.splitext(filename)
10 | dir_path = path.dirname(path.realpath(filename))
11 |
12 | with open(path.join(dir_path, test_name, file)) as f:
13 | return f.read()
14 |
15 |
16 | def get_fixture_file_data(testcase, file):
17 | return json.loads(get_fixture_file(testcase, file))
18 |
19 |
20 | def execute_skybot_regex(command_method, inp, **kwargs):
21 | for type, (func, func_args) in command_method._hook:
22 | if not type == "regex" or "re" not in func_args:
23 | continue
24 |
25 | search_result = func_args["re"].search(inp)
26 |
27 | if search_result:
28 | return command_method(search_result, **kwargs)
29 |
30 | return None
31 |
--------------------------------------------------------------------------------
/test/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmmh/skybot/76eca17bbc23e3e0b262a6b533eac4d58d55e5d1/test/plugins/__init__.py
--------------------------------------------------------------------------------
/test/plugins/test_bf.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from bf import bf
3 |
4 |
5 | class TestBF(TestCase):
6 | def test_hello(self):
7 | expected = "Hello world!"
8 | actual = bf(
9 | "--[>--->->->++>-<<<<<-------]>--.>---------.>--..+++.>---"
10 | "-.>+++++++++.<<.+++.------.<-.>>+."
11 | )
12 |
13 | assert expected == actual
14 |
15 | def test_unbalanced(self):
16 | expected = "unbalanced brackets"
17 |
18 | actual_a = bf("[[++]]]")
19 | actual_b = bf("[[[++]]")
20 |
21 | assert expected == actual_a
22 | assert expected == actual_b
23 |
24 | def test_comment(self):
25 | expected = "*"
26 | actual = bf("[this is a comment!]++++++[>+++++++<-]>.")
27 |
28 | assert expected == actual
29 |
30 | def test_unprintable(self):
31 | expected = "no printable output"
32 | actual = bf("+.")
33 |
34 | assert expected == actual
35 |
36 | def test_empty(self):
37 | expected = "no output"
38 | actual = bf("+++[-]")
39 |
40 | assert expected == actual
41 |
42 | def test_exceeded(self):
43 | expected = "no output [exceeded 1000 iterations]"
44 | actual = bf("+[>,[-]<]", 1000)
45 |
46 | assert expected == actual
47 |
48 | def test_inf_mem(self):
49 | expected = "no output [exceeded 1000 iterations]"
50 | actual = bf("+[>[.-]+]", 1000, buffer_size=10)
51 |
52 | assert expected == actual
53 |
54 | def test_left_wrap(self):
55 | # eventually, wrap around and hit ourselves
56 | expected = "aaaaaaa [exceeded 2000 iterations]"
57 | actual = bf("+[<[-" + "+" * ord("a") + ".[-]]+]", 2000, buffer_size=5)
58 |
59 | assert expected == actual
60 |
61 | def test_too_much_output(self):
62 | expected = "a" * 430
63 | actual = bf("+" * ord("a") + "[.]")
64 |
65 | assert expected == actual
66 |
--------------------------------------------------------------------------------
/test/plugins/test_bitcoin.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from mock import patch, Mock
3 |
4 | from helpers import get_fixture_file_data
5 | from bitcoin import bitcoin, ethereum
6 |
7 |
8 | class TestBitcoin(TestCase):
9 | @patch("util.http.get_json")
10 | def test_bitcoin(self, mock_http_get):
11 | mock_http_get.return_value = get_fixture_file_data(self, "bitcoin.json")
12 |
13 | say_mock = Mock()
14 |
15 | bitcoin("", say=say_mock)
16 |
17 | expected = (
18 | "USD/BTC: \x0307$6,390.67\x0f - High: \x0307$6,627."
19 | "00\x0f - Low: \x0307$6,295.73\x0f - Volume: "
20 | "10,329.67 BTC"
21 | )
22 | say_mock.assert_called_once_with(expected)
23 |
24 | @patch("util.http.get_json")
25 | def test_ethereum(self, mock_http_get):
26 | mock_http_get.return_value = get_fixture_file_data(self, "ethereum.json")
27 |
28 | say_mock = Mock()
29 |
30 | ethereum("", say=say_mock)
31 |
32 | expected = (
33 | "USD/ETH: \x0307$355.02\x0f - High: \x0307$370.43"
34 | "\x0f - Low: \x0307$350.00\x0f - Volume: "
35 | "16,408.97 ETH"
36 | )
37 |
38 | say_mock.assert_called_once_with(expected)
39 |
--------------------------------------------------------------------------------
/test/plugins/test_bitcoin/bitcoin.json:
--------------------------------------------------------------------------------
1 | {"high": "6627.00000000", "last": "6390.67", "timestamp": "1533932533", "bid": "6390.66", "vwap": "6432.04", "volume": "10329.66980521", "low": "6295.73000000", "ask": "6390.67", "open": 6544.98}
--------------------------------------------------------------------------------
/test/plugins/test_bitcoin/ethereum.json:
--------------------------------------------------------------------------------
1 | {"high": "370.43", "last": "355.02", "timestamp": "1533932548", "bid": "354.53", "vwap": "361.09", "volume": "16408.96753222", "low": "350.00", "ask": "354.98", "open": "363.96"}
--------------------------------------------------------------------------------
/test/plugins/test_cdecl.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from mock import patch
3 |
4 | from cdecl import cdecl
5 |
6 |
7 | class TestCDecl(TestCase):
8 | @patch("util.http.get")
9 | def test_cdecl(self, mock_http_get):
10 | mock_http_get.side_effect = [
11 | 'var QUERY_ENDPOINT = "http://foo.bar"',
12 | '"declare x as array 3 of pointer to function returning pointer to array 5 of char"',
13 | ]
14 |
15 | expected = '"declare x as array 3 of pointer to function returning pointer to array 5 of char"'
16 | actual = cdecl("char (*(*x())[5])()")
17 |
18 | assert expected == actual
19 |
--------------------------------------------------------------------------------
/test/plugins/test_choose.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from mock import patch
3 |
4 | from choose import choose
5 |
6 |
7 | class TestChoose(TestCase):
8 | def test_choose_one_choice(self):
9 | expected = "the decision is up to you"
10 | actual = choose("foo")
11 |
12 | assert expected == actual
13 |
14 | def test_choose_same_thing(self):
15 | expected = "foo"
16 | actual = choose("foo, foo, foo")
17 |
18 | assert expected == actual
19 |
20 | def test_choose_two_choices(self):
21 | actual = choose("foo, bar")
22 |
23 | assert actual in ["foo", "bar"]
24 |
25 | def test_choose_choices_space(self):
26 | expected_values = ["foo", "bar"]
27 | actual = choose("foo bar")
28 |
29 | assert actual in expected_values
30 |
31 | def test_choose_strips_whitespace(self):
32 | expected_values = ["foo", "bar"]
33 | actual = choose(" foo ," " bar ")
34 |
35 | assert actual in expected_values
36 |
37 | @patch("random.choice")
38 | def test_choose_end_comma_behavior(self, mock_random_choice):
39 | mock_random_choice.side_effect = lambda arr: arr[0]
40 |
41 | expected = "the decision is up to you"
42 | actual = choose("foo,")
43 |
44 | assert actual == expected
45 |
46 | @patch("random.choice")
47 | def test_choose_collapse_commas(self, mock_random_choice):
48 | # Should never be an empty string here
49 | mock_random_choice.side_effect = lambda arr: arr[1]
50 |
51 | expected = "bar"
52 | actual = choose("foo,,bar")
53 |
54 | assert actual == expected
55 |
--------------------------------------------------------------------------------
/test/plugins/test_crowdcontrol.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 | from unittest import TestCase
3 | from mock import Mock, patch, call
4 |
5 | from helpers import execute_skybot_regex
6 | from crowdcontrol import crowdcontrol
7 |
8 |
9 | class TestCrowdcontrol(TestCase):
10 | def call_crowd_control(self, input, called=None, rules=None):
11 | mock_names = ["kick", "ban", "unban", "reply"]
12 |
13 | mocks = {}
14 |
15 | for name in mock_names:
16 | mocks[name] = Mock(name=name)
17 |
18 | config = {"crowdcontrol": rules} if rules else {}
19 | bot = namedtuple("Bot", "config")(config=config)
20 |
21 | execute_skybot_regex(crowdcontrol, input, bot=bot, **mocks)
22 |
23 | return mocks
24 |
25 | def test_no_rules(self):
26 | mocks = self.call_crowd_control("Hello world!")
27 |
28 | for m in mocks.values():
29 | m.assert_not_called()
30 |
31 | def test_no_matches(self):
32 | mocks = self.call_crowd_control("Hello world!", rules=[{"re": "No match"}])
33 |
34 | for m in mocks.values():
35 | m.assert_not_called()
36 |
37 | def test_match_no_action(self):
38 | mocks = self.call_crowd_control("Hello world!", rules=[{"re": "Hello"}])
39 |
40 | for m in mocks.values():
41 | m.assert_not_called()
42 |
43 | def test_match_only_msg(self):
44 | mocks = self.call_crowd_control(
45 | "Hello world!", rules=[{"re": "Hello", "msg": "Hello!"}]
46 | )
47 |
48 | for n, m in mocks.items():
49 | if n == "reply":
50 | m.assert_called_once_with("Hello!")
51 | else:
52 | m.assert_not_called()
53 |
54 | def test_match_ban_forever_no_kick(self):
55 | mocks = self.call_crowd_control(
56 | "Hello world!", rules=[{"re": "Hello", "ban_length": -1}]
57 | )
58 |
59 | for n, m in mocks.items():
60 | if n == "ban":
61 | m.assert_called_once()
62 | else:
63 | m.assert_not_called()
64 |
65 | def test_match_kick_no_ban(self):
66 | mocks = self.call_crowd_control(
67 | "Hello world!", rules=[{"re": "Hello", "kick": 1}]
68 | )
69 |
70 | for n, m in mocks.items():
71 | if n == "kick":
72 | m.assert_called_once()
73 | else:
74 | m.assert_not_called()
75 |
76 | def test_match_kick_with_msg(self):
77 | mocks = self.call_crowd_control(
78 | "Hello world!", rules=[{"re": "Hello", "kick": 1, "msg": "Hello!"}]
79 | )
80 |
81 | for n, m in mocks.items():
82 | if n == "kick":
83 | m.assert_called_once_with(reason="Hello!")
84 | else:
85 | m.assert_not_called()
86 |
87 | def test_match_kick_ban_forever(self):
88 | mocks = self.call_crowd_control(
89 | "Hello world!", rules=[{"re": "Hello", "kick": 1, "ban_length": -1}]
90 | )
91 |
92 | for n, m in mocks.items():
93 | if n == "kick" or n == "ban":
94 | m.assert_called_once()
95 | else:
96 | m.assert_not_called()
97 |
98 | @patch("time.sleep")
99 | def test_match_ban_only_time_limit(self, mock_time_sleep):
100 |
101 | mocks = self.call_crowd_control(
102 | "Hello world!", rules=[{"re": "Hello", "ban_length": 5}]
103 | )
104 |
105 | mock_time_sleep.assert_called_once_with(5)
106 |
107 | for n, m in mocks.items():
108 | if n == "ban" or n == "unban":
109 | m.assert_called_once()
110 | else:
111 | m.assert_not_called()
112 |
113 | @patch("time.sleep")
114 | def test_match_kick_ban_time_limit(self, mock_time_sleep):
115 |
116 | mocks = self.call_crowd_control(
117 | "Hello world!", rules=[{"re": "Hello", "kick": 1, "ban_length": 5}]
118 | )
119 |
120 | mock_time_sleep.assert_called_once_with(5)
121 |
122 | for n, m in mocks.items():
123 | if n == "kick" or n == "ban" or n == "unban":
124 | m.assert_called_once()
125 | else:
126 | m.assert_not_called()
127 |
128 | def test_match_multiple_rules_in_order(self):
129 | mocks = self.call_crowd_control(
130 | "Hello world!",
131 | rules=[
132 | {"re": "Hello", "msg": "1"},
133 | {"re": "Fancy", "msg": "2"},
134 | {"re": "[wW]orld", "msg": "3"},
135 | ],
136 | )
137 |
138 | for n, m in mocks.items():
139 | if n == "reply":
140 | m.assert_has_calls([call("1"), call("3")])
141 | else:
142 | m.assert_not_called()
143 |
--------------------------------------------------------------------------------
/test/plugins/test_crypto.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from mock import Mock, patch
3 |
4 | from helpers import get_fixture_file_data
5 | from crypto import crypto
6 |
7 |
8 | class TestCrypto(TestCase):
9 | @patch("util.http.get_json")
10 | def test_crypto_btc_to_default(self, mock_http_get):
11 | mock_http_get.return_value = get_fixture_file_data(
12 | self, "crypto_btc_to_default.json"
13 | )
14 |
15 | expected = (
16 | u"USD/\u0243: \x0307$ 6,084.30\x0f - High: "
17 | u"\x0307$ 6,178.90\x0f - Low: \x0307$ 6,014.26"
18 | u"\x0f - Volume: \u0243 21,428.7 "
19 | u"($ 131,067,493.3) - Total Supply: "
20 | u"\u0243 17,202,675.0 - MktCap: $ 104.67 B"
21 | )
22 |
23 | say_mock = Mock()
24 | crypto("btc", say=say_mock)
25 |
26 | say_mock.assert_called_once_with(expected)
27 |
28 | @patch("util.http.get_json")
29 | def test_crypto_eth_to_default(self, mock_http_get):
30 | mock_http_get.return_value = get_fixture_file_data(
31 | self, "crypto_eth_to_default.json"
32 | )
33 |
34 | expected = (
35 | u"USD/\u039e: \x0307$ 315.91\x0f - High: "
36 | u"\x0307$ 332.41\x0f - Low: \x0307$ 312.35\x0f "
37 | u"- Volume: \u039e 163,011.9 ($ 52,513,531.9) - "
38 | u"Total Supply: \u039e 101,251,550.4 - "
39 | u"MktCap: $ 31.99 B"
40 | )
41 |
42 | say_mock = Mock()
43 | crypto("eth", say_mock)
44 |
45 | say_mock.assert_called_once_with(expected)
46 |
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_btc_to_btc.json:
--------------------------------------------------------------------------------
1 | {"RAW":{"BTC":{"BTC":{"TYPE":"5","MARKET":"CCCAGG","FROMSYMBOL":"BTC","TOSYMBOL":"BTC","FLAGS":"4","PRICE":1,"LASTUPDATE":1533965230,"LASTVOLUME":0,"LASTVOLUMETO":0,"LASTTRADEID":0,"VOLUMEDAY":0,"VOLUMEDAYTO":0,"VOLUME24HOUR":0,"VOLUME24HOURTO":0,"OPENDAY":0.9579629629629629,"HIGHDAY":1.000193348801237,"LOWDAY":0.9563690146052872,"OPEN24HOUR":0.9247407937075438,"HIGH24HOUR":1.002713704206242,"LOW24HOUR":0.913957597173145,"LASTMARKET":"Cexio","CHANGE24HOUR":0.0752592062924562,"CHANGEPCT24HOUR":8.138410980088922,"CHANGEDAY":0.042037037037037095,"CHANGEPCTDAY":4.388169340808048,"SUPPLY":17202675,"MKTCAP":17202675,"TOTALVOLUME24H":462329.4035614009,"TOTALVOLUME24HTO":462329.4035614009}}},"DISPLAY":{"BTC":{"BTC":{"FROMSYMBOL":"Ƀ","TOSYMBOL":"Ƀ","MARKET":"CryptoCompare Index","PRICE":"Ƀ 1.00","LASTUPDATE":"Just now","LASTVOLUME":"Ƀ 0","LASTVOLUMETO":"Ƀ 0","LASTTRADEID":0,"VOLUMEDAY":"Ƀ 0","VOLUMEDAYTO":"Ƀ 0","VOLUME24HOUR":"Ƀ 0","VOLUME24HOURTO":"Ƀ 0","OPENDAY":"Ƀ 0.9580","HIGHDAY":"Ƀ 1.00","LOWDAY":"Ƀ 0.9564","OPEN24HOUR":"Ƀ 0.9247","HIGH24HOUR":"Ƀ 1.00","LOW24HOUR":"Ƀ 0.9140","LASTMARKET":"Cexio","CHANGE24HOUR":"Ƀ 0.075","CHANGEPCT24HOUR":"8.14","CHANGEDAY":"Ƀ 0.042","CHANGEPCTDAY":"4.39","SUPPLY":"Ƀ 17,202,675.0","MKTCAP":"Ƀ 17.20 M","TOTALVOLUME24H":"Ƀ 462.33 K","TOTALVOLUME24HTO":"Ƀ 462.33 K"}}}}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_btc_to_cad.json:
--------------------------------------------------------------------------------
1 | {"RAW":{"BTC":{"CAD":{"TYPE":"5","MARKET":"CCCAGG","FROMSYMBOL":"BTC","TOSYMBOL":"CAD","FLAGS":"4","PRICE":8608.76,"LASTUPDATE":1533964566,"LASTVOLUME":0.00278958,"LASTVOLUMETO":24.3886563366,"LASTTRADEID":"3562813","VOLUMEDAY":54.70759288999996,"VOLUMEDAYTO":468402.7143309134,"VOLUME24HOUR":412.1872334000001,"VOLUME24HOURTO":3594321.404294858,"OPENDAY":8647.55,"HIGHDAY":8816.09,"LOWDAY":8459.29,"OPEN24HOUR":8921.47,"HIGH24HOUR":8989.06,"LOW24HOUR":8491.39,"LASTMARKET":"QuadrigaCX","CHANGE24HOUR":-312.7099999999991,"CHANGEPCT24HOUR":-3.505139848029519,"CHANGEDAY":-38.789999999999054,"CHANGEPCTDAY":-0.4485663569450198,"SUPPLY":17202675,"MKTCAP":148093700433,"TOTALVOLUME24H":458413.46110797784,"TOTALVOLUME24HTO":3946417367.8848057}}},"DISPLAY":{"BTC":{"CAD":{"FROMSYMBOL":"Ƀ","TOSYMBOL":"CAD","MARKET":"CryptoCompare Index","PRICE":"CAD 8,608.76","LASTUPDATE":"1 min ago","LASTVOLUME":"Ƀ 0.002790","LASTVOLUMETO":"CAD 24.39","LASTTRADEID":"3562813","VOLUMEDAY":"Ƀ 54.71","VOLUMEDAYTO":"CAD 468,402.7","VOLUME24HOUR":"Ƀ 412.19","VOLUME24HOURTO":"CAD 3,594,321.4","OPENDAY":"CAD 8,647.55","HIGHDAY":"CAD 8,816.09","LOWDAY":"CAD 8,459.29","OPEN24HOUR":"CAD 8,921.47","HIGH24HOUR":"CAD 8,989.06","LOW24HOUR":"CAD 8,491.39","LASTMARKET":"QuadrigaCX","CHANGE24HOUR":"CAD -312.71","CHANGEPCT24HOUR":"-3.51","CHANGEDAY":"CAD -38.79","CHANGEPCTDAY":"-0.45","SUPPLY":"Ƀ 17,202,675.0","MKTCAP":"CAD 148.09 B","TOTALVOLUME24H":"Ƀ 458.41 K","TOTALVOLUME24HTO":"CAD 3,946.42 M"}}}}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_btc_to_default.json:
--------------------------------------------------------------------------------
1 | {"RAW":{"BTC":{"USD":{"TYPE":"5","MARKET":"CCCAGG","FROMSYMBOL":"BTC","TOSYMBOL":"USD","FLAGS":"1","PRICE":6084.3,"LASTUPDATE":1533965210,"LASTVOLUME":0.0049,"LASTVOLUMETO":29.968890000000002,"LASTTRADEID":"7779588","VOLUMEDAY":21428.734067810907,"VOLUMEDAYTO":131067493.30908191,"VOLUME24HOUR":105750.41163186147,"VOLUME24HOURTO":663220908.6037983,"OPENDAY":6153.41,"HIGHDAY":6178.9,"LOWDAY":6014.26,"OPEN24HOUR":6452.85,"HIGH24HOUR":6541.03,"LOW24HOUR":6009.51,"LASTMARKET":"Cexio","CHANGE24HOUR":-368.5500000000002,"CHANGEPCT24HOUR":-5.711429833329461,"CHANGEDAY":-69.10999999999967,"CHANGEPCTDAY":-1.1231171009245227,"SUPPLY":17202675,"MKTCAP":104666235502.5,"TOTALVOLUME24H":462035.95442587347,"TOTALVOLUME24HTO":2830969036.625406}}},"DISPLAY":{"BTC":{"USD":{"FROMSYMBOL":"Ƀ","TOSYMBOL":"$","MARKET":"CryptoCompare Index","PRICE":"$ 6,084.30","LASTUPDATE":"Just now","LASTVOLUME":"Ƀ 0.004900","LASTVOLUMETO":"$ 29.97","LASTTRADEID":"7779588","VOLUMEDAY":"Ƀ 21,428.7","VOLUMEDAYTO":"$ 131,067,493.3","VOLUME24HOUR":"Ƀ 105,750.4","VOLUME24HOURTO":"$ 663,220,908.6","OPENDAY":"$ 6,153.41","HIGHDAY":"$ 6,178.90","LOWDAY":"$ 6,014.26","OPEN24HOUR":"$ 6,452.85","HIGH24HOUR":"$ 6,541.03","LOW24HOUR":"$ 6,009.51","LASTMARKET":"Cexio","CHANGE24HOUR":"$ -368.55","CHANGEPCT24HOUR":"-5.71","CHANGEDAY":"$ -69.11","CHANGEPCTDAY":"-1.12","SUPPLY":"Ƀ 17,202,675.0","MKTCAP":"$ 104.67 B","TOTALVOLUME24H":"Ƀ 462.04 K","TOTALVOLUME24HTO":"$ 2,830.97 M"}}}}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_btc_to_eth.json:
--------------------------------------------------------------------------------
1 | {"RAW":{"BTC":{"ETH":{"TYPE":"5","MARKET":"CCCAGG","FROMSYMBOL":"BTC","TOSYMBOL":"ETH","FLAGS":"4","PRICE":18.01,"LASTUPDATE":1533961008,"LASTVOLUME":0.0003108,"LASTVOLUMETO":0.005656949403826,"LASTTRADEID":"162309","VOLUMEDAY":0.1679238600000003,"VOLUMEDAYTO":3.087088803783464,"VOLUME24HOUR":0.36601415000000004,"VOLUME24HOURTO":6.624954482906522,"OPENDAY":17.08,"HIGHDAY":18.9,"LOWDAY":17.08,"OPEN24HOUR":17.02,"HIGH24HOUR":18.9,"LOW24HOUR":16.98,"LASTMARKET":"OpenLedger","CHANGE24HOUR":0.990000000000002,"CHANGEPCT24HOUR":5.816686251468872,"CHANGEDAY":0.9300000000000033,"CHANGEPCTDAY":5.444964871194399,"SUPPLY":17202675,"MKTCAP":309820176.75,"TOTALVOLUME24H":459191.20724751457,"TOTALVOLUME24HTO":8270033.675567379}}},"DISPLAY":{"BTC":{"ETH":{"FROMSYMBOL":"Ƀ","TOSYMBOL":"Ξ","MARKET":"CryptoCompare Index","PRICE":"Ξ 18.01","LASTUPDATE":"1 hour ago","LASTVOLUME":"Ƀ 0.0003108","LASTVOLUMETO":"Ξ 0.005657","LASTTRADEID":"162309","VOLUMEDAY":"Ƀ 0.1679","VOLUMEDAYTO":"Ξ 3.09","VOLUME24HOUR":"Ƀ 0.3660","VOLUME24HOURTO":"Ξ 6.62","OPENDAY":"Ξ 17.08","HIGHDAY":"Ξ 18.90","LOWDAY":"Ξ 17.08","OPEN24HOUR":"Ξ 17.02","HIGH24HOUR":"Ξ 18.90","LOW24HOUR":"Ξ 16.98","LASTMARKET":"OpenLedger","CHANGE24HOUR":"Ξ 0.99","CHANGEPCT24HOUR":"5.82","CHANGEDAY":"Ξ 0.93","CHANGEPCTDAY":"5.44","SUPPLY":"Ƀ 17,202,675.0","MKTCAP":"Ξ 309.82 M","TOTALVOLUME24H":"Ƀ 459.19 K","TOTALVOLUME24HTO":"Ξ 8,270.03 K"}}}}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_btc_to_invalid.json:
--------------------------------------------------------------------------------
1 | {"Response":"Error","Message":"There is no data for any of the toSymbols INVALID .","Type":1,"Aggregated":false,"Data":[],"Warning":"There is no data for the toSymbol/s INVALID ","HasWarning":true}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_eth_to_default.json:
--------------------------------------------------------------------------------
1 | {"RAW":{"ETH":{"USD":{"TYPE":"5","MARKET":"CCCAGG","FROMSYMBOL":"ETH","TOSYMBOL":"USD","FLAGS":"4","PRICE":315.91,"LASTUPDATE":1533964817,"LASTVOLUME":0.44537967,"LASTVOLUMETO":140.63753839589998,"LASTTRADEID":"278603674","VOLUMEDAY":163011.90779591043,"VOLUMEDAYTO":52513531.941159755,"VOLUME24HOUR":555836.3497450602,"VOLUME24HOURTO":188832440.6669058,"OPENDAY":331.57,"HIGHDAY":332.41,"LOWDAY":312.35,"OPEN24HOUR":359.95,"HIGH24HOUR":363.96,"LOW24HOUR":311.86,"LASTMARKET":"Bitfinex","CHANGE24HOUR":-44.039999999999964,"CHANGEPCT24HOUR":-12.235032643422688,"CHANGEDAY":-15.659999999999968,"CHANGEPCTDAY":-4.722984588473013,"SUPPLY":101251550.374,"MKTCAP":31986377278.65034,"TOTALVOLUME24H":3396774.5259628375,"TOTALVOLUME24HTO":1086313219.915864}}},"DISPLAY":{"ETH":{"USD":{"FROMSYMBOL":"Ξ","TOSYMBOL":"$","MARKET":"CryptoCompare Index","PRICE":"$ 315.91","LASTUPDATE":"Just now","LASTVOLUME":"Ξ 0.4454","LASTVOLUMETO":"$ 140.64","LASTTRADEID":"278603674","VOLUMEDAY":"Ξ 163,011.9","VOLUMEDAYTO":"$ 52,513,531.9","VOLUME24HOUR":"Ξ 555,836.3","VOLUME24HOURTO":"$ 188,832,440.7","OPENDAY":"$ 331.57","HIGHDAY":"$ 332.41","LOWDAY":"$ 312.35","OPEN24HOUR":"$ 359.95","HIGH24HOUR":"$ 363.96","LOW24HOUR":"$ 311.86","LASTMARKET":"Bitfinex","CHANGE24HOUR":"$ -44.04","CHANGEPCT24HOUR":"-12.24","CHANGEDAY":"$ -15.66","CHANGEPCTDAY":"-4.72","SUPPLY":"Ξ 101,251,550.4","MKTCAP":"$ 31.99 B","TOTALVOLUME24H":"Ξ 3,396.77 K","TOTALVOLUME24HTO":"$ 1,086.31 M"}}}}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/crypto_invalid_to_usd.json:
--------------------------------------------------------------------------------
1 | {"Response":"Error","Message":"There is no data for any of the toSymbols INVALID .","Type":1,"Aggregated":false,"Data":[],"Warning":"There is no data for the toSymbol/s INVALID ","HasWarning":true}
--------------------------------------------------------------------------------
/test/plugins/test_crypto/example.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmmh/skybot/76eca17bbc23e3e0b262a6b533eac4d58d55e5d1/test/plugins/test_crypto/example.json
--------------------------------------------------------------------------------
/test/plugins/test_dice.py:
--------------------------------------------------------------------------------
1 | from unittest import skip, TestCase
2 | from mock import patch
3 |
4 | from dice import dice
5 |
6 |
7 | class TestDice(TestCase):
8 | @patch("random.randint")
9 | def test_one_d20(self, mock_random_randint):
10 | mock_random_randint.return_value = 5
11 |
12 | expected = "5 (d20=5)"
13 | actual = dice("d20")
14 |
15 | assert expected == actual
16 |
17 | @skip("skip until https://github.com/rmmh/skybot/pull/187 is merged")
18 | @patch("random.randint")
19 | def test_complex_roll(self, mock_random_randint):
20 | mock_random_randint.side_effect = iter([1, 2, 3])
21 |
22 | expected = u"4 (2d20-d5+4=1, 2, -3)"
23 | actual = dice("2d20-d5+4")
24 |
25 | assert expected == actual
26 |
27 | def test_constant_roll(self):
28 | # This fails so it is "none"
29 | actual = dice("1234")
30 |
31 | assert actual is None
32 |
33 | @patch("random.randint")
34 | def test_fudge_dice(self, mock_random_randint):
35 | mock_random_randint.side_effect = iter([-1, 0, 1, 0, -1])
36 |
37 | expected = "-1 (5dF=\x034-\x0f, 0, \x033+\x0f, 0, \x034-\x0f)"
38 | actual = dice("5dF")
39 |
40 | assert expected == actual
41 |
42 | @patch("random.randint")
43 | def test_fudge_dice(self, mock_random_randint):
44 | mock_random_randint.side_effect = iter([-1, 0, 1, 0, -1])
45 |
46 | expected = "-1 (5dF=\x034-\x0f, 0, \x033+\x0f, 0, \x034-\x0f)"
47 | actual = dice("5dF")
48 |
49 | assert expected == actual
50 |
--------------------------------------------------------------------------------
/test/plugins/test_hackernews.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from mock import patch
4 |
5 | from helpers import get_fixture_file_data, execute_skybot_regex
6 | from hackernews import hackernews
7 |
8 |
9 | class TestHackernews(TestCase):
10 | @patch("util.http.get_json")
11 | def test_story(self, mock_http_get):
12 | mock_http_get.return_value = get_fixture_file_data(self, "9943431.json")
13 |
14 | expected = (
15 | u"Can Desalination Counter the Drought? by cwal37 "
16 | u"with 51 points and 94 comments "
17 | u"(http://www.newyorker.com/tech/elements/can-"
18 | u"desalination-counter-the-drought)"
19 | )
20 |
21 | url = "https://news.ycombinator.com/item?id=9943431"
22 | actual = execute_skybot_regex(hackernews, url)
23 |
24 | assert expected == actual
25 |
26 | @patch("util.http.get_json")
27 | def test_comment(self, mock_http_get):
28 | mock_http_get.return_value = get_fixture_file_data(self, "9943987.json")
29 |
30 | expected = (
31 | u'"Yes, they must have meant kilowatt hours. Was '
32 | u'there no editor?" -- oaktowner'
33 | )
34 |
35 | url = "https://news.ycombinator.com/item?id=9943987"
36 | actual = execute_skybot_regex(hackernews, url)
37 |
38 | assert expected == actual
39 |
40 | @patch("util.http.get_json")
41 | def test_comment_encoding(self, mock_http_get):
42 | mock_http_get.return_value = get_fixture_file_data(self, "9943897.json")
43 |
44 | expected = (
45 | u'"> All told, it takes about 3460 kilowatts per '
46 | u"acre-foot to pump water from Northern California "
47 | u"to San Diego; Carlsbad will use about thirty per "
48 | u"cent more energy, five thousand kilowatts per "
49 | u"acre-foot, to desalinate ocean water and deliver "
50 | u"it to households, according to Poseidon\u2019s "
51 | u"report to the Department of Water Resources // "
52 | u"These units are abominations. Couldn't just "
53 | u"say 2.8 Watts per liter vs 4.0 Watts per liter? "
54 | u"Or even 10.6 and 15.3 Watts per gallon? I'm "
55 | u"not a metric purist, but the only advantage to "
56 | u"using imperial units is that they are more "
57 | u"familiar to the average American, but when does "
58 | u'the average person deal with acre-feet?" '
59 | u"-- alwaysdoit"
60 | )
61 |
62 | url = "https://news.ycombinator.com/item?id=9943897"
63 | actual = execute_skybot_regex(hackernews, url)
64 |
65 | assert expected == actual
66 |
--------------------------------------------------------------------------------
/test/plugins/test_hackernews/9943431.json:
--------------------------------------------------------------------------------
1 | {"by":"cwal37","descendants":94,"id":9943431,"kids":[9943897,9943970,9945149,9943728,9943555,9944002,9944159,9943505,9944946,9944207,9944377,9944089],"score":51,"time":1437757443,"title":"Can Desalination Counter the Drought?","type":"story","url":"http://www.newyorker.com/tech/elements/can-desalination-counter-the-drought"}
--------------------------------------------------------------------------------
/test/plugins/test_hackernews/9943897.json:
--------------------------------------------------------------------------------
1 | {"by":"alwaysdoit","id":9943897,"kids":[9943947,9943932,9944612,9944060,9944161,9944100,9945559],"parent":9943431,"text":"> All told, it takes about 3460 kilowatts per acre-foot to pump water from Northern California to San Diego; Carlsbad will use about thirty per cent more energy, five thousand kilowatts per acre-foot, to desalinate ocean water and deliver it to households, according to Poseidon’s report to the Department of Water ResourcesThese units are abominations. Couldn't just say 2.8 Watts per liter vs 4.0 Watts per liter? Or even 10.6 and 15.3 Watts per gallon? I'm not a metric purist, but the only advantage to using imperial units is that they are more familiar to the average American, but when does the average person deal with acre-feet?","time":1437760700,"type":"comment"}
--------------------------------------------------------------------------------
/test/plugins/test_hackernews/9943987.json:
--------------------------------------------------------------------------------
1 | {"by":"oaktowner","id":9943987,"kids":[9944095],"parent":9943932,"text":"Yes, they must have meant kilowatt hours. Was there no editor?","time":1437761436,"type":"comment"}
--------------------------------------------------------------------------------
/test/plugins/test_twitter.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 |
4 | from mock import patch
5 |
6 | from helpers import get_fixture_file_data, execute_skybot_regex
7 | from twitter import show_tweet, twitter
8 |
9 |
10 | FAKE_API_KEY = {
11 | "consumer": "AAAAAAAAAAAAAAAAAAAAAAAAA",
12 | "consumer_secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
13 | "access": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
14 | "access_secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
15 | }
16 |
17 |
18 | class TestTwitter(TestCase):
19 | @patch("util.http.get_json")
20 | def test_tweet_regex(self, mock_http_get):
21 | mock_http_get.return_value = get_fixture_file_data(
22 | self, "1014260007771295745.json"
23 | )
24 |
25 | expected = (
26 | u"2018-07-03 21:30:04 \x02jk_rowling\x02: "
27 | u"hahahahahahahahahahahahahahahahahahahahaha"
28 | u"hahahahahahahahahahahahahahahahahahahahaha"
29 | u"hahahahahahahahahahahahahahahahahahahahaha"
30 | u"hahahaha "
31 | u"*draws breath* "
32 | u"hahahahahahahahahahahahahahahahahahahahaha"
33 | u"hahahahahahahahahahahahahahahahahahahahaha"
34 | u"hahahahahahahahahahahahahahahahahahahahaha"
35 | u"haha https://t.co/gbionAPK9Z"
36 | )
37 |
38 | url = "https://twitter.com/jk_rowling/status/1014260007771295745"
39 | actual = execute_skybot_regex(show_tweet, url, api_key=FAKE_API_KEY)
40 |
41 | assert expected == actual
42 |
43 | @patch("util.http.get_json")
44 | def test_twitter_username_no_tweet_number(self, mock_http_get):
45 | mock_http_get.return_value = get_fixture_file_data(
46 | self, "user_loneblockbuster.json"
47 | )
48 |
49 | expected = (
50 | u"2018-08-16 17:38:52 \x02loneblockbuster\x02: "
51 | u"We had the motto \"When you're here you're "
52 | u'family" before Olive Garden but like '
53 | u"everything else it was taken away "
54 | u"from us."
55 | )
56 |
57 | actual = twitter("loneblockbuster", api_key=FAKE_API_KEY)
58 |
59 | assert expected == actual
60 |
61 | @patch("util.http.get_json")
62 | def test_twitter_username_with_tweet_number(self, mock_http_get):
63 | mock_http_get.return_value = get_fixture_file_data(
64 | self, "user_loneblockbuster.json"
65 | )
66 |
67 | expected = (
68 | u"2018-07-24 19:30:59 \x02loneblockbuster\x02: "
69 | u"We never would have planted the ferns out "
70 | u"front if we knew we'd get so many death threats."
71 | )
72 |
73 | actual = twitter("loneblockbuster 10", api_key=FAKE_API_KEY)
74 |
75 | assert expected == actual
76 |
77 | @patch("random.randint")
78 | @patch("util.http.get_json")
79 | def test_twitter_hashtag_no_tweet_number(self, mock_http_get, mock_random_randint):
80 | mock_http_get.return_value = get_fixture_file_data(self, "hashtag_nyc.json")
81 |
82 | # This plugin chooses a random value.
83 | # I chose this value randomly by rolling a D20.
84 | mock_random_randint.return_value = 6
85 |
86 | expected = (
87 | u"2018-08-17 20:19:56 \x02The_Carl_John\x02: "
88 | u"RT @ItVisn How many records can your "
89 | u"company afford to lose? https://t.co/iJzFYtJmCh "
90 | u"Be proactive and Protect Your Business "
91 | u"#CyberSecurity #DataProtection "
92 | u"#DataBreaches #SmallBusiness #Tech "
93 | u"#Data #NYC #Technology #DarkWeb #Ransomware "
94 | u"#Malware #Phishing #Business "
95 | u"https://t.co/xAJVRhjOww"
96 | )
97 |
98 | actual = twitter("#NYC", api_key=FAKE_API_KEY)
99 |
100 | assert expected == actual
101 |
102 | @patch("util.http.get_json")
103 | def test_twitter_hashtag_with_tweet_number(self, mock_http_get):
104 | mock_http_get.return_value = get_fixture_file_data(self, "hashtag_nyc.json")
105 |
106 | expected = (
107 | u"2018-08-17 20:19:32 \x02Kugey\x02: "
108 | u"I know for sure that life is beautiful "
109 | u"around the world... \u2022 \u2022 Here "
110 | u"is yet another iconic piece of the "
111 | u"NYC skyline... \u2022 \u2022 #nyc "
112 | u"#photography #streetphotography "
113 | u"#urbanphotography\u2026 "
114 | u"https://t.co/bq9i0FZN89"
115 | )
116 |
117 | actual = twitter("#NYC 10", api_key=FAKE_API_KEY)
118 |
119 | assert expected == actual
120 |
--------------------------------------------------------------------------------
/test/plugins/test_twitter/1014260007771295745.json:
--------------------------------------------------------------------------------
1 | {"created_at":"Tue Jul 03 21:30:04 +0000 2018","id":1014260007771295745,"id_str":"1014260007771295745","full_text":"hahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahaha *draws breath* hahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahaha https:\/\/t.co\/gbionAPK9Z","truncated":false,"display_text_range":[0,280],"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[{"url":"https:\/\/t.co\/gbionAPK9Z","expanded_url":"https:\/\/twitter.com\/realDonaldTrump\/status\/1014257237945176071","display_url":"twitter.com\/realDonaldTrum\u2026","indices":[281,304]}]},"source":"\u003ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c\/a\u003e","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":62513246,"id_str":"62513246","name":"J.K. Rowling","screen_name":"jk_rowling","location":"Scotland","description":"Writer","url":"https:\/\/t.co\/7iaKMs3iC6","entities":{"url":{"urls":[{"url":"https:\/\/t.co\/7iaKMs3iC6","expanded_url":"http:\/\/www.jkrowling.com","display_url":"jkrowling.com","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":14354031,"friends_count":621,"listed_count":37861,"created_at":"Mon Aug 03 13:23:45 +0000 2009","favourites_count":22020,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":true,"statuses_count":10691,"lang":"en","contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme9\/bg.gif","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme9\/bg.gif","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/1029136897506004992\/9xvEQiO0_normal.jpg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/1029136897506004992\/9xvEQiO0_normal.jpg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/62513246\/1534200280","profile_link_color":"C0C0C0","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"000000","profile_text_color":"000000","profile_use_background_image":true,"has_extended_profile":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false,"translator_type":"regular"},"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":true,"retweet_count":34885,"favorite_count":158199,"favorited":false,"retweeted":false,"possibly_sensitive":false,"possibly_sensitive_appealable":false,"lang":"tl"}
2 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27,py37
3 | skipsdist=True
4 |
5 | [testenv]
6 | commands = pip install -qqq lxml future
7 | pip install -qqq mock==2.0.0
8 | python test
9 |
--------------------------------------------------------------------------------