Merge branch 'multi' into Dockerfile-modify
This commit is contained in:
commit
2bd870f29e
|
|
@ -1,3 +1,5 @@
|
|||
configs/*.ini
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
.vscode/settings.json
|
||||
|
|
@ -6,3 +8,7 @@ __pycache__
|
|||
dependencies
|
||||
testing
|
||||
lookup_route.py
|
||||
icao_url_gen.py
|
||||
install.sh
|
||||
coul_icao_gen.py
|
||||
test.py
|
||||
|
|
|
|||
9
Pipfile
9
Pipfile
|
|
@ -18,7 +18,14 @@ selenium = "*"
|
|||
opensky-api = {editable = true, git = "https://github.com/openskynetwork/opensky-api.git", subdirectory = "python"}
|
||||
webdriver-manager = "*"
|
||||
shapely = "*"
|
||||
pycairo = "*"
|
||||
geog = "*"
|
||||
py-staticmaps = "*"
|
||||
pyproj = "*"
|
||||
pandas = "*"
|
||||
lxml = "*"
|
||||
beautifulsoup4 = "*"
|
||||
python-telegram-bot = "*"
|
||||
|
||||
configparser = "*"
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "5131229ab384051accd51e665cc63da8e2d08a651ad0c9df09041fca9f306977"
|
||||
"sha256": "35921a1158bc5b11d88e9f2a4702ee3ae09228a7d5438d1562ba62f24b02fd05"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
|
@ -16,51 +16,214 @@
|
|||
]
|
||||
},
|
||||
"default": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
|
||||
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
|
||||
],
|
||||
"version": "==1.4.4"
|
||||
},
|
||||
"apscheduler": {
|
||||
"hashes": [
|
||||
"sha256:3bb5229eed6fbbdafc13ce962712ae66e175aa214c69bed35a06bffcf0c5e244",
|
||||
"sha256:e8b1ecdb4c7cb2818913f766d5898183c7cb8936680710a4d3a966e02262e526"
|
||||
],
|
||||
"version": "==3.6.3"
|
||||
},
|
||||
"async-generator": {
|
||||
"hashes": [
|
||||
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
|
||||
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.10"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836",
|
||||
"sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==22.2.0"
|
||||
},
|
||||
"beautifulsoup4": {
|
||||
"hashes": [
|
||||
"sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30",
|
||||
"sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.11.1"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
|
||||
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
|
||||
],
|
||||
"markers": "python_version ~= '3.5'",
|
||||
"version": "==4.2.2"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
|
||||
"sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
],
|
||||
"version": "==2021.10.8"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.12.7"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
|
||||
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
|
||||
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
|
||||
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
|
||||
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
|
||||
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
|
||||
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
|
||||
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
|
||||
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
|
||||
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
|
||||
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
|
||||
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
|
||||
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
|
||||
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
|
||||
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
|
||||
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
|
||||
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
|
||||
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
|
||||
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
|
||||
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
|
||||
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
|
||||
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
|
||||
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
|
||||
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
|
||||
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
|
||||
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
|
||||
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
|
||||
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
|
||||
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
|
||||
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
|
||||
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
|
||||
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
|
||||
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
|
||||
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
|
||||
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
|
||||
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
|
||||
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
|
||||
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
|
||||
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
|
||||
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
|
||||
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
|
||||
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
|
||||
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
|
||||
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
|
||||
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
|
||||
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
|
||||
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
|
||||
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
|
||||
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
|
||||
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
|
||||
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
|
||||
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
|
||||
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
|
||||
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
|
||||
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
|
||||
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
|
||||
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
|
||||
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
|
||||
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
|
||||
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
|
||||
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
|
||||
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
|
||||
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
|
||||
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
|
||||
],
|
||||
"markers": "os_name == 'nt' and implementation_name != 'pypy'",
|
||||
"version": "==1.15.1"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
|
||||
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
|
||||
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
|
||||
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==2.0.12"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
|
||||
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
|
||||
"sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da",
|
||||
"sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.4.4"
|
||||
"version": "==0.4.5"
|
||||
},
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:1b35798fdf1713f1c3139016cfcbc461f09edbf099d1fb658d4b7479fcaa3daa",
|
||||
"sha256:e8b39238fb6f0153a069aa253d349467c3c4737934f253ef6abac5fe0eca1e5d"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"index": "pypi",
|
||||
"version": "==5.2.0"
|
||||
},
|
||||
"crayons": {
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:bd33b7547800f2cfbd26b38431f9e64b487a7de74a947b0fafc89b45a601813f",
|
||||
"sha256:e73ad105c78935d71fe454dd4b85c5c437ba199294e7ffd3341842bc683654b1"
|
||||
"sha256:0e70da4bdff7601b0ef48e6348339e490ebfb0cbe638e083c9c41fb49f00c8bd",
|
||||
"sha256:10652dd7282de17990b88679cb82f832752c4e8237f0c714be518044269415db",
|
||||
"sha256:175c1a818b87c9ac80bb7377f5520b7f31b3ef2a0004e2420319beadedb67290",
|
||||
"sha256:1d7e632804a248103b60b16fb145e8df0bc60eed790ece0d12efe8cd3f3e7744",
|
||||
"sha256:1f13ddda26a04c06eb57119caf27a524ccae20533729f4b1e4a69b54e07035eb",
|
||||
"sha256:2ec2a8714dd005949d4019195d72abed84198d877112abb5a27740e217e0ea8d",
|
||||
"sha256:2fa36a7b2cc0998a3a4d5af26ccb6273f3df133d61da2ba13b3286261e7efb70",
|
||||
"sha256:2fb481682873035600b5502f0015b664abc26466153fab5c6bc92c1ea69d478b",
|
||||
"sha256:3178d46f363d4549b9a76264f41c6948752183b3f587666aff0555ac50fd7876",
|
||||
"sha256:4367da5705922cf7070462e964f66e4ac24162e22ab0a2e9d31f1b270dd78083",
|
||||
"sha256:4eb85075437f0b1fd8cd66c688469a0c4119e0ba855e3fef86691971b887caf6",
|
||||
"sha256:50a1494ed0c3f5b4d07650a68cd6ca62efe8b596ce743a5c94403e6f11bf06c1",
|
||||
"sha256:53049f3379ef05182864d13bb9686657659407148f901f3f1eee57a733fb4b00",
|
||||
"sha256:6391e59ebe7c62d9902c24a4d8bcbc79a68e7c4ab65863536127c8a9cd94043b",
|
||||
"sha256:67461b5ebca2e4c2ab991733f8ab637a7265bb582f07c7c88914b5afb88cb95b",
|
||||
"sha256:78e47e28ddc4ace41dd38c42e6feecfdadf9c3be2af389abbfeef1ff06822285",
|
||||
"sha256:80ca53981ceeb3241998443c4964a387771588c4e4a5d92735a493af868294f9",
|
||||
"sha256:8a4b2bdb68a447fadebfd7d24855758fe2d6fecc7fed0b78d190b1af39a8e3b0",
|
||||
"sha256:8e45653fb97eb2f20b8c96f9cd2b3a0654d742b47d638cf2897afbd97f80fa6d",
|
||||
"sha256:998cd19189d8a747b226d24c0207fdaa1e6658a1d3f2494541cb9dfbf7dcb6d2",
|
||||
"sha256:a10498349d4c8eab7357a8f9aa3463791292845b79597ad1b98a543686fb1ec8",
|
||||
"sha256:b4cad0cea995af760f82820ab4ca54e5471fc782f70a007f31531957f43e9dee",
|
||||
"sha256:bfe6472507986613dc6cc00b3d492b2f7564b02b3b3682d25ca7f40fa3fd321b",
|
||||
"sha256:c9e0d79ee4c56d841bd4ac6e7697c8ff3c8d6da67379057f29e66acffcd1e9a7",
|
||||
"sha256:ca57eb3ddaccd1112c18fc80abe41db443cc2e9dcb1917078e02dfa010a4f353",
|
||||
"sha256:ce127dd0a6a0811c251a6cddd014d292728484e530d80e872ad9806cfb1c5b3c"
|
||||
],
|
||||
"version": "==0.4.0"
|
||||
"version": "==38.0.4"
|
||||
},
|
||||
"discord-webhook": {
|
||||
"hashes": [
|
||||
"sha256:17e475d8a52fe0bfa26b071925f55087600e9bb96e821b611dc463f4b4998c89",
|
||||
"sha256:f3d660df572caaa9c2621edd7e8634a70d6d8295ce9256c365838312457069a1"
|
||||
"sha256:5a044a26ceea6e39651c2fa17194b463dc0b704b8b20991e8db3f3d86dd5e553",
|
||||
"sha256:e9434e516a918e78915b5ce32b864d5b3f24556b92e1606bcad6a4f83f5615bd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.0"
|
||||
"version": "==0.16.3"
|
||||
},
|
||||
"exceptiongroup": {
|
||||
"hashes": [
|
||||
"sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828",
|
||||
"sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.18.2"
|
||||
},
|
||||
"geog": {
|
||||
"hashes": [
|
||||
"sha256:9b6b020b72bf135d49299115e5a4a751f2432def4d4cd87d10b48a5ae51ec643"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.2"
|
||||
},
|
||||
"geographiclib": {
|
||||
"hashes": [
|
||||
|
|
@ -77,68 +240,247 @@
|
|||
"index": "pypi",
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
|
||||
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.14.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.3"
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.4"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318",
|
||||
"sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c",
|
||||
"sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b",
|
||||
"sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000",
|
||||
"sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73",
|
||||
"sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d",
|
||||
"sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb",
|
||||
"sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8",
|
||||
"sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2",
|
||||
"sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345",
|
||||
"sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94",
|
||||
"sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e",
|
||||
"sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b",
|
||||
"sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc",
|
||||
"sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a",
|
||||
"sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9",
|
||||
"sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc",
|
||||
"sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387",
|
||||
"sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb",
|
||||
"sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7",
|
||||
"sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4",
|
||||
"sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97",
|
||||
"sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67",
|
||||
"sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627",
|
||||
"sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7",
|
||||
"sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd",
|
||||
"sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3",
|
||||
"sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7",
|
||||
"sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130",
|
||||
"sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b",
|
||||
"sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036",
|
||||
"sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785",
|
||||
"sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca",
|
||||
"sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91",
|
||||
"sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc",
|
||||
"sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536",
|
||||
"sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391",
|
||||
"sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3",
|
||||
"sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d",
|
||||
"sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21",
|
||||
"sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3",
|
||||
"sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d",
|
||||
"sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29",
|
||||
"sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715",
|
||||
"sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed",
|
||||
"sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25",
|
||||
"sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c",
|
||||
"sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785",
|
||||
"sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837",
|
||||
"sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4",
|
||||
"sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b",
|
||||
"sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2",
|
||||
"sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067",
|
||||
"sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448",
|
||||
"sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d",
|
||||
"sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2",
|
||||
"sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc",
|
||||
"sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c",
|
||||
"sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5",
|
||||
"sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84",
|
||||
"sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8",
|
||||
"sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf",
|
||||
"sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7",
|
||||
"sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e",
|
||||
"sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb",
|
||||
"sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b",
|
||||
"sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3",
|
||||
"sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad",
|
||||
"sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8",
|
||||
"sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.9.1"
|
||||
},
|
||||
"numpy": {
|
||||
"hashes": [
|
||||
"sha256:0104d8adaa3a4cc60c2777cab5196593bf8a7f416eda133be1f3803dd0838886",
|
||||
"sha256:0885d9a7666cafe5f9876c57bfee34226e2b2847bfb94c9505e18d81011e5401",
|
||||
"sha256:12bba5561d8118981f2f1ff069ecae200c05d7b6c78a5cdac0911f74bc71cbd1",
|
||||
"sha256:2f8e0df2ecc1928ef7256f18e309c9d6229b08b5be859163f5caa59c93d53646",
|
||||
"sha256:4445f472b246cad6514cc09fbb5ecb7aab09ca2acc3c16f29f8dca6c468af501",
|
||||
"sha256:4d01f7832fa319a36fd75ba10ea4027c9338ede875792f7bf617f4b45056fc3a",
|
||||
"sha256:4f5e78b8b710cd7cd1a8145994cfffc6ddd5911669a437777d8cedfce6c83a98",
|
||||
"sha256:667b5b1f6a352419e340f6475ef9930348ae5cb7fca15f2cc3afcb530823715e",
|
||||
"sha256:6e73a1f4f5b74a42abb55bc2b3d869f1b38cbc8776da5f8b66bf110284f7a437",
|
||||
"sha256:73cf2c5b5a07450f20a0c8e04d9955491970177dce8df8d6903bf253e53268e0",
|
||||
"sha256:7ad6a024a32ee61d18f5b402cd02e9c0e22c0fb9dc23751991b3a16d209d972e",
|
||||
"sha256:8b1ddfac6a82d4f3c8e99436c90b9c2c68c0bb14658d1684cdd00f05fab241f5",
|
||||
"sha256:90075ef2c6ac6397d0035bcd8b298b26e481a7035f7a3f382c047eb9c3414db0",
|
||||
"sha256:9387c7d6d50e8f8c31e7bfc034241e9c6f4b3eb5db8d118d6487047b922f82af",
|
||||
"sha256:9af91f794d2d3007d91d749ebc955302889261db514eb24caef30e03e8ec1e41",
|
||||
"sha256:ab11f6a7602cf8ea4c093e091938207de3068c5693a0520168ecf4395750f7ea",
|
||||
"sha256:ac4fe68f1a5a18136acebd4eff91aab8bed00d1ef2fdb34b5d9192297ffbbdfc",
|
||||
"sha256:ada6c1e9608ceadaf7020e1deea508b73ace85560a16f51bef26aecb93626a72",
|
||||
"sha256:c4ab7c9711fe6b235e86487ca74c1b092a6dd59a3cb45b63241ea0a148501853",
|
||||
"sha256:cec79ff3984b2d1d103183fc4a3361f5b55bbb66cb395cbf5a920a4bb1fd588d",
|
||||
"sha256:cf8960f72997e56781eb1c2ea256a70124f92a543b384f89e5fb3503a308b1d3",
|
||||
"sha256:d7f223554aba7280e6057727333ed357b71b7da7422d02ff5e91b857888c25d1",
|
||||
"sha256:dbb0490f0a880700a6cc4d000384baf19c1f4df59fff158d9482d4dbbca2b239",
|
||||
"sha256:e63d2157f9fc98cc178870db83b0e0c85acdadd598b134b00ebec9e0db57a01f",
|
||||
"sha256:ec3e5e8172a0a6a4f3c2e7423d4a8434c41349141b04744b11a90e017a95bad5",
|
||||
"sha256:f3c4a9a9f92734a4728ddbd331e0124eabbc968a0359a506e8e74a9b0d2d419b",
|
||||
"sha256:f9168790149f917ad8e3cf5047b353fefef753bd50b07c547da0bdf30bc15d91",
|
||||
"sha256:fe44e925c68fb5e8db1334bf30ac1a1b6b963b932a19cf41d2e899cf02f36aab"
|
||||
],
|
||||
"markers": "python_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64'",
|
||||
"version": "==1.24.0"
|
||||
},
|
||||
"oauthlib": {
|
||||
"hashes": [
|
||||
"sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2",
|
||||
"sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"
|
||||
"sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca",
|
||||
"sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.2.0"
|
||||
"version": "==3.2.2"
|
||||
},
|
||||
"opensky-api": {
|
||||
"editable": true,
|
||||
"git": "https://github.com/openskynetwork/opensky-api.git",
|
||||
"ref": "d576cf260affd99156e352528ea46817273512d7",
|
||||
"ref": "7bda8d14ada6241f1f246fd42d7d032aabe7ea91",
|
||||
"subdirectory": "python"
|
||||
},
|
||||
"outcome": {
|
||||
"hashes": [
|
||||
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
|
||||
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"pandas": {
|
||||
"hashes": [
|
||||
"sha256:07238a58d7cbc8a004855ade7b75bbd22c0db4b0ffccc721556bab8a095515f6",
|
||||
"sha256:0daf876dba6c622154b2e6741f29e87161f844e64f84801554f879d27ba63c0d",
|
||||
"sha256:16ad23db55efcc93fa878f7837267973b61ea85d244fc5ff0ccbcfa5638706c5",
|
||||
"sha256:1d9382f72a4f0e93909feece6fef5500e838ce1c355a581b3d8f259839f2ea76",
|
||||
"sha256:24ea75f47bbd5574675dae21d51779a4948715416413b30614c1e8b480909f81",
|
||||
"sha256:2893e923472a5e090c2d5e8db83e8f907364ec048572084c7d10ef93546be6d1",
|
||||
"sha256:2ff7788468e75917574f080cd4681b27e1a7bf36461fe968b49a87b5a54d007c",
|
||||
"sha256:41fc406e374590a3d492325b889a2686b31e7a7780bec83db2512988550dadbf",
|
||||
"sha256:48350592665ea3cbcd07efc8c12ff12d89be09cd47231c7925e3b8afada9d50d",
|
||||
"sha256:605d572126eb4ab2eadf5c59d5d69f0608df2bf7bcad5c5880a47a20a0699e3e",
|
||||
"sha256:6dfbf16b1ea4f4d0ee11084d9c026340514d1d30270eaa82a9f1297b6c8ecbf0",
|
||||
"sha256:6f803320c9da732cc79210d7e8cc5c8019aad512589c910c66529eb1b1818230",
|
||||
"sha256:721a3dd2f06ef942f83a819c0f3f6a648b2830b191a72bbe9451bcd49c3bd42e",
|
||||
"sha256:755679c49460bd0d2f837ab99f0a26948e68fa0718b7e42afbabd074d945bf84",
|
||||
"sha256:78b00429161ccb0da252229bcda8010b445c4bf924e721265bec5a6e96a92e92",
|
||||
"sha256:958a0588149190c22cdebbc0797e01972950c927a11a900fe6c2296f207b1d6f",
|
||||
"sha256:a3924692160e3d847e18702bb048dc38e0e13411d2b503fecb1adf0fcf950ba4",
|
||||
"sha256:d51674ed8e2551ef7773820ef5dab9322be0828629f2cbf8d1fc31a0c4fed640",
|
||||
"sha256:d5ebc990bd34f4ac3c73a2724c2dcc9ee7bf1ce6cf08e87bb25c6ad33507e318",
|
||||
"sha256:d6c0106415ff1a10c326c49bc5dd9ea8b9897a6ca0c8688eb9c30ddec49535ef",
|
||||
"sha256:e48fbb64165cda451c06a0f9e4c7a16b534fcabd32546d531b3c240ce2844112"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"pillow": {
|
||||
"hashes": [
|
||||
"sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97",
|
||||
"sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049",
|
||||
"sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c",
|
||||
"sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae",
|
||||
"sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28",
|
||||
"sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030",
|
||||
"sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56",
|
||||
"sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976",
|
||||
"sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e",
|
||||
"sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e",
|
||||
"sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f",
|
||||
"sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b",
|
||||
"sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a",
|
||||
"sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e",
|
||||
"sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa",
|
||||
"sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7",
|
||||
"sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00",
|
||||
"sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838",
|
||||
"sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360",
|
||||
"sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b",
|
||||
"sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a",
|
||||
"sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd",
|
||||
"sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4",
|
||||
"sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70",
|
||||
"sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204",
|
||||
"sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc",
|
||||
"sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b",
|
||||
"sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669",
|
||||
"sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7",
|
||||
"sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e",
|
||||
"sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c",
|
||||
"sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092",
|
||||
"sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c",
|
||||
"sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5",
|
||||
"sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"
|
||||
"sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040",
|
||||
"sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8",
|
||||
"sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65",
|
||||
"sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2",
|
||||
"sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627",
|
||||
"sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07",
|
||||
"sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef",
|
||||
"sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535",
|
||||
"sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c",
|
||||
"sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc",
|
||||
"sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3",
|
||||
"sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1",
|
||||
"sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c",
|
||||
"sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa",
|
||||
"sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32",
|
||||
"sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502",
|
||||
"sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4",
|
||||
"sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f",
|
||||
"sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812",
|
||||
"sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636",
|
||||
"sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20",
|
||||
"sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c",
|
||||
"sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91",
|
||||
"sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe",
|
||||
"sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b",
|
||||
"sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad",
|
||||
"sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9",
|
||||
"sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72",
|
||||
"sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4",
|
||||
"sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de",
|
||||
"sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29",
|
||||
"sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee",
|
||||
"sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c",
|
||||
"sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7",
|
||||
"sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11",
|
||||
"sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c",
|
||||
"sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c",
|
||||
"sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448",
|
||||
"sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b",
|
||||
"sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20",
|
||||
"sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228",
|
||||
"sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd",
|
||||
"sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699",
|
||||
"sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b",
|
||||
"sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2",
|
||||
"sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4",
|
||||
"sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c",
|
||||
"sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f",
|
||||
"sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2",
|
||||
"sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c",
|
||||
"sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3",
|
||||
"sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193",
|
||||
"sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48",
|
||||
"sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02",
|
||||
"sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8",
|
||||
"sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e",
|
||||
"sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f",
|
||||
"sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b",
|
||||
"sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74",
|
||||
"sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb",
|
||||
"sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==9.0.1"
|
||||
"version": "==9.3.0"
|
||||
},
|
||||
"pushbullet.py": {
|
||||
"hashes": [
|
||||
|
|
@ -148,29 +490,144 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"py-staticmaps": {
|
||||
"hashes": [
|
||||
"sha256:5aba5ad59f30a63f860e76ed99407a6efb24eaad5c8997aa8617363989f17389"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.4.0"
|
||||
},
|
||||
"pycairo": {
|
||||
"hashes": [
|
||||
"sha256:251907f18a552df938aa3386657ff4b5a4937dde70e11aa042bc297957f4b74b",
|
||||
"sha256:26b72b813c6f9d495f71057eab89c13e70a21c92360e9265abc049e0a931fa39",
|
||||
"sha256:31e1c4850db03201d33929cbe1905ce1b33202683ebda7bb0d4dba489115066b",
|
||||
"sha256:4357f20a6b1de8f1e8072a74ff68ab4c9a0ae698cd9f5c0f2b2cdd9b28b635f6",
|
||||
"sha256:44a2ecf34968de07b3b9dfdcdbccbd25aa3cab267200f234f84e81481a73bbf6",
|
||||
"sha256:6d37375aab9f2bb6136f076c19815d72108383baae89fbc0d6cb8e5092217d02",
|
||||
"sha256:70936b19f967fa3cb3cd200c2608911227fa5d09dae21c166f64bc15e714ee41",
|
||||
"sha256:dace6b356c476de27f8e1522428ac21a799c225703f746e2957d441f885dcb6c",
|
||||
"sha256:f63c153a9ea3d21aff85e2caeee4b0c5d566b2368b4ed64826020d12953d76a4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.21.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
|
||||
],
|
||||
"version": "==2.21"
|
||||
},
|
||||
"pyopenssl": {
|
||||
"hashes": [
|
||||
"sha256:7a83b7b272dd595222d672f5ce29aa030f1fb837630ef229f62e72e395ce8968",
|
||||
"sha256:b28437c9773bb6c6958628cf9c3bebe585de661dba6f63df17111966363dd15e"
|
||||
],
|
||||
"version": "==22.1.0"
|
||||
},
|
||||
"pyproj": {
|
||||
"hashes": [
|
||||
"sha256:07c9d8d7ec009bbac09e233cfc725601586fe06880e5538a3a44eaf560ba3a62",
|
||||
"sha256:120d45ed73144c65e9677dc73ba8a531c495d179dd9f9f0471ac5acc02d7ac4b",
|
||||
"sha256:140fa649fedd04f680a39f8ad339799a55cb1c49f6a84e1b32b97e49646647aa",
|
||||
"sha256:1adc9ccd1bf04998493b6a2e87e60656c75ab790653b36cfe351e9ef214828ed",
|
||||
"sha256:1ef1bfbe2dcc558c7a98e2f1836abdcd630390f3160724a6f4f5c818b2be0ad5",
|
||||
"sha256:2fef9c1e339f25c57f6ae0558b5ab1bbdf7994529a30d8d7504fc6302ea51c03",
|
||||
"sha256:3cc4771403db54494e1e55bca8e6d33cde322f8cf0ed39f1557ff109c66d2cd1",
|
||||
"sha256:42eea10afc750fccd1c5c4ba56de29ab791ab4d83c1f7db72705566282ac5396",
|
||||
"sha256:45487942c19c5a8b09c91964ea3201f4e094518e34743cae373889a36e3d9260",
|
||||
"sha256:473961faef7a9fd723c5d432f65220ea6ab3854e606bf84b4d409a75a4261c78",
|
||||
"sha256:52efb681647dfac185cc655a709bc0caaf910031a0390f816f5fc8ce150cbedc",
|
||||
"sha256:531ea36519fa7b581466d4b6ab32f66ae4dadd9499d726352f71ee5e19c3d1c5",
|
||||
"sha256:56b0f9ee2c5b2520b18db30a393a7b86130cf527ddbb8c96e7f3c837474a9d79",
|
||||
"sha256:5ab0d6e38fda7c13726afacaf62e9f9dd858089d67910471758afd9cb24e0ecd",
|
||||
"sha256:5ca5f32b56210429b367ca4f9a57ffe67975c487af82e179a24370879a3daf68",
|
||||
"sha256:5dac03d4338a4c8bd0f69144c527474f517b4cbd7d2d8c532cd8937799723248",
|
||||
"sha256:5f92d8f6514516124abb714dce912b20867831162cfff9fae2678ef07b6fcf0f",
|
||||
"sha256:67025e37598a6bbed2c9c6c9e4c911f6dd39315d3e1148ead935a5c4d64309d5",
|
||||
"sha256:797ad5655d484feac14b0fbb4a4efeaac0cf780a223046e2465494c767fd1c3b",
|
||||
"sha256:aba199704c824fb84ab64927e7bc9ef71e603e483130ec0f7e09e97259b8f61f",
|
||||
"sha256:aed1a3c0cd4182425f91b48d5db39f459bc2fe0d88017ead6425a1bc85faee33",
|
||||
"sha256:b3d8e14d91cc95fb3dbc03a9d0588ac58326803eefa5bbb0978d109de3304fbe",
|
||||
"sha256:b59c08aea13ee428cf8a919212d55c036cc94784805ed77c8f31a4d1f541058c",
|
||||
"sha256:c99f7b5757a28040a2dd4a28c9805fdf13eef79a796f4a566ab5cb362d10630d",
|
||||
"sha256:f1032e5dfb50eae06382bcc7b9011b994f7104d932fe91bd83a722275e30e8ce"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"pysocks": {
|
||||
"hashes": [
|
||||
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
|
||||
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
|
||||
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
|
||||
],
|
||||
"version": "==1.7.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5",
|
||||
"sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.21.0"
|
||||
},
|
||||
"python-magic": {
|
||||
"hashes": [
|
||||
"sha256:1a2c81e8f395c744536369790bd75094665e9644110a6623bcc3bbea30f03973",
|
||||
"sha256:21f5f542aa0330f5c8a64442528542f6215c8e18d2466b399b0d9d39356d83fc"
|
||||
"sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b",
|
||||
"sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.4.25"
|
||||
"version": "==0.4.27"
|
||||
},
|
||||
"python-slugify": {
|
||||
"hashes": [
|
||||
"sha256:003aee64f9fd955d111549f96c4b58a3f40b9319383c70fad6277a4974bbf570",
|
||||
"sha256:7a0f21a39fa6c1c4bf2e5984c9b9ae944483fd10b54804cb0e23a3ccd4954f0b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==7.0.0"
|
||||
},
|
||||
"python-telegram-bot": {
|
||||
"hashes": [
|
||||
"sha256:13f83ec4433f24a67e7a2df0206dfde83c1627c92880a2fcf95a83585b3cc589",
|
||||
"sha256:4296d81a38b7e5ef1f9795651128e58fb354678b8dc4db93ca166c96828c57b2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==13.13"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
|
||||
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
|
||||
"sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
|
||||
"sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2021.1"
|
||||
"version": "==2022.1"
|
||||
},
|
||||
"pytz-deprecation-shim": {
|
||||
"hashes": [
|
||||
"sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6",
|
||||
"sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==0.1.0.post0"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
|
||||
"sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
|
||||
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
|
||||
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==2.27.1"
|
||||
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||
"version": "==2.28.1"
|
||||
},
|
||||
"requests-oauthlib": {
|
||||
"hashes": [
|
||||
|
|
@ -180,82 +637,222 @@
|
|||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"s2sphere": {
|
||||
"hashes": [
|
||||
"sha256:c2478c1ff7c601a59a7151a57b605435897514578fa6bdb8730721c182adbbaf",
|
||||
"sha256:d2340c9cf458ddc9a89afd1d8048a4195ce6fa6b0095ab900d4be5271e537401"
|
||||
],
|
||||
"version": "==0.2.5"
|
||||
},
|
||||
"selenium": {
|
||||
"hashes": [
|
||||
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
|
||||
"sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
|
||||
"sha256:f67402b8f973aaa98d9c55b8f9aa63532009cd1859b2222a8b9800354942d8bc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.141.0"
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54",
|
||||
"sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==65.6.3"
|
||||
},
|
||||
"shapely": {
|
||||
"hashes": [
|
||||
"sha256:052eb5b9ba756808a7825e8a8020fb146ec489dd5c919e7d139014775411e688",
|
||||
"sha256:1641724c1055459a7e2b8bbe47ba25bdc89554582e62aec23cb3f3ca25f9b129",
|
||||
"sha256:17df66e87d0fe0193910aeaa938c99f0b04f67b430edb8adae01e7be557b141b",
|
||||
"sha256:182716ffb500d114b5d1b75d7fd9d14b7d3414cef3c38c0490534cc9ce20981a",
|
||||
"sha256:2df5260d0f2983309776cb41bfa85c464ec07018d88c0ecfca23d40bfadae2f1",
|
||||
"sha256:35be1c5d869966569d3dfd4ec31832d7c780e9df760e1fe52131105685941891",
|
||||
"sha256:46da0ea527da9cf9503e66c18bab6981c5556859e518fe71578b47126e54ca93",
|
||||
"sha256:4c10f317e379cc404f8fc510cd9982d5d3e7ba13a9cfd39aa251d894c6366798",
|
||||
"sha256:4f3c59f6dbf86a9fc293546de492f5e07344e045f9333f3a753f2dda903c45d1",
|
||||
"sha256:60e5b2282619249dbe8dc5266d781cc7d7fb1b27fa49f8241f2167672ad26719",
|
||||
"sha256:617bf046a6861d7c6b44d2d9cb9e2311548638e684c2cd071d8945f24a926263",
|
||||
"sha256:6593026cd3f5daaea12bcc51ae5c979318070fefee210e7990cb8ac2364e79a1",
|
||||
"sha256:6871acba8fbe744efa4f9f34e726d070bfbf9bffb356a8f6d64557846324232b",
|
||||
"sha256:791477edb422692e7dc351c5ed6530eb0e949a31b45569946619a0d9cd5f53cb",
|
||||
"sha256:8e7659dd994792a0aad8fb80439f59055a21163e236faf2f9823beb63a380e19",
|
||||
"sha256:8f15b6ce67dcc05b61f19c689b60f3fe58550ba994290ff8332f711f5aaa9840",
|
||||
"sha256:90a3e2ae0d6d7d50ff2370ba168fbd416a53e7d8448410758c5d6a5920646c1d",
|
||||
"sha256:a3774516c8a83abfd1ddffb8b6ec1b0935d7fe6ea0ff5c31a18bfdae567b4eba",
|
||||
"sha256:a5c3a50d823c192f32615a2a6920e8c046b09e07a58eba220407335a9cd2e8ea",
|
||||
"sha256:b40cc7bb089ae4aa9ddba1db900b4cd1bce3925d2a4b5837b639e49de054784f",
|
||||
"sha256:da38ed3d65b8091447dc3717e5218cc336d20303b77b0634b261bc5c1aa2bae8",
|
||||
"sha256:de618e67b64a51a0768d26a9963ecd7d338a2cf6e9e7582d2385f88ad005b3d1",
|
||||
"sha256:e3afccf0437edc108eef1e2bb9cc4c7073e7705924eb4cd0bf7715cd1ef0ce1b"
|
||||
"sha256:0c0fd457ce477b1dced507a72f1e2084c9191bfcb8a1e09886990ebd02acf024",
|
||||
"sha256:137f1369630408024a62ff79a437a5657e6c5b76b9cd352dde704b425acdb298",
|
||||
"sha256:15a856fbb588ad5d042784e00918c662902776452008c771ecba2ff615cd197a",
|
||||
"sha256:1d95842cc6bbbeab673061b63e70b07be9a375c15a60f4098f8fbd29f43af1b4",
|
||||
"sha256:256bdf8080bb7bb504d47b2c76919ecebab9708cc1b26266b3ec32b42448f642",
|
||||
"sha256:2e02da2e988e74d61f15c720f9f613fab51942aae2dfeacdcb78eadece00e1f3",
|
||||
"sha256:3423299254deec075e79fb7dc7909d702104e4167149de7f45510c3a6342eeea",
|
||||
"sha256:3a40bf497b57a6625b83996aed10ce2233bca0e5471b8af771b186d681433ac5",
|
||||
"sha256:44d2832c1b706bf43101fda92831a083467cc4b4923a7ed17319ab599c1025d8",
|
||||
"sha256:5254240eefc44139ab0d128faf671635d8bdd9c23955ee063d4d6b8f20073ae0",
|
||||
"sha256:56413f7d32c70b63f239eb0865b24c0c61029e38757de456cc4ab3c416559a0b",
|
||||
"sha256:572af9d5006fd5e3213e37ee548912b0341fb26724d6dc8a4e3950c10197ebb6",
|
||||
"sha256:62056e64b12b6d483d79f8e34bf058d2fe734d51c9227c1713705399434eff3b",
|
||||
"sha256:68c8e18dc9dc8a198c3addc8c9596f64137101f566f04b96ecfca0b214cb8b12",
|
||||
"sha256:6bdc7728f1e5df430d8c588661f79f1eed4a2728c8b689e12707cfec217f68f8",
|
||||
"sha256:6fcb28836ae93809de1dde73c03c9c24bab0ba2b2bf419ddb2aeb72c96d110e9",
|
||||
"sha256:75042e8039c79dd01f102bb288beace9dc2f49fc44a2dea875f9b697aa8cd30d",
|
||||
"sha256:78966332a89813b237de357a03f612fd451a871fe6e26c12b6b71645fe8eee39",
|
||||
"sha256:7c8eda45085ccdd7f9805ea4a93fdd5eb0b6039a61d5f0cefb960487e6dc17a1",
|
||||
"sha256:7c9e3400b716c51ba43eea1678c28272580114e009b6c78cdd00c44df3e325fa",
|
||||
"sha256:840be3f27a1152851c54b968f2e12d718c9f13b7acd51c482e58a70f60f29e31",
|
||||
"sha256:8e3ed52a081da58eb4a885c157c594876633dbd4eb283f13ba5bf39c82322d76",
|
||||
"sha256:8fe641f1f61b3d43dd61b5a85d2ef023e6e19bf8f204a5160a1cb1ec645cbc09",
|
||||
"sha256:a58e1f362f2091743e5e13212f5d5d16251a4bb63dd0ed587c652d3be9620d3a",
|
||||
"sha256:a60861b5ca2c488ebcdc706eca94d325c26d1567921c74acc83df5e6913590c7",
|
||||
"sha256:beee3949ddf381735049cfa6532fb234d5d20a5be910c4f2fb7c7295fd7960e3",
|
||||
"sha256:c0a0d7752b145343838bd36ed09382d85f5befe426832d7384c5b051c147acbd",
|
||||
"sha256:c60f3758212ec480675b820b13035dda8af8f7cc560d2cc67999b2717fb8faef",
|
||||
"sha256:ce0b5c5f7acbccf98b3460eecaa40e9b18272b2a734f74fcddf1d7696e047e95",
|
||||
"sha256:cec89a5617c0137f4678282e983c3d63bf838fb00cdf318cc555b4d8409f7130",
|
||||
"sha256:d3f3fac625690f01f35af665649e993f15f924e740b5c0ac0376900655815521",
|
||||
"sha256:d74de394684d66e25e780b0359fda85be7766af85940fa2dfad728b1a815c71f",
|
||||
"sha256:e07b0bd2a0e61a8afd4d1c1bd23f3550b711f01274ffb53de99358fd781eefd8",
|
||||
"sha256:f12695662c3ad1e6031b3de98f191963d0f09de6d1a4988acd907405644032ba"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.7.1"
|
||||
"version": "==1.8.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"sniffio": {
|
||||
"hashes": [
|
||||
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
|
||||
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"sortedcontainers": {
|
||||
"hashes": [
|
||||
"sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88",
|
||||
"sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"
|
||||
],
|
||||
"version": "==2.4.0"
|
||||
},
|
||||
"soupsieve": {
|
||||
"hashes": [
|
||||
"sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759",
|
||||
"sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.3.2.post1"
|
||||
},
|
||||
"svgwrite": {
|
||||
"hashes": [
|
||||
"sha256:a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3",
|
||||
"sha256:bb6b2b5450f1edbfa597d924f9ac2dd099e625562e492021d7dd614f65f8a22d"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"tabulate": {
|
||||
"hashes": [
|
||||
"sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4",
|
||||
"sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"
|
||||
"sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc",
|
||||
"sha256:436f1c768b424654fce8597290d2764def1eea6a77cfa5c33be00b1bc0f4f63d",
|
||||
"sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.8.9"
|
||||
"version": "==0.8.10"
|
||||
},
|
||||
"text-unidecode": {
|
||||
"hashes": [
|
||||
"sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
|
||||
"sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
|
||||
],
|
||||
"version": "==1.3"
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
"sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca",
|
||||
"sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72",
|
||||
"sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23",
|
||||
"sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8",
|
||||
"sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b",
|
||||
"sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9",
|
||||
"sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13",
|
||||
"sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75",
|
||||
"sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac",
|
||||
"sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e",
|
||||
"sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.2"
|
||||
},
|
||||
"trio": {
|
||||
"hashes": [
|
||||
"sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
|
||||
"sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.22.0"
|
||||
},
|
||||
"trio-websocket": {
|
||||
"hashes": [
|
||||
"sha256:5b558f6e83cc20a37c3b61202476c5295d1addf57bd65543364e0337e37ed2bc",
|
||||
"sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==0.9.2"
|
||||
},
|
||||
"tweepy": {
|
||||
"hashes": [
|
||||
"sha256:b28d72073b794141ad1cfdc34cb52b83b14d6b34cd4c5ea9d47bb85159b656e8",
|
||||
"sha256:bd3af045bed9cd6a838c32a1344d48191096e5cb930012452f8397b12c89d785"
|
||||
"sha256:7f92574920c2f233663fff154745fc2bb0d10aedc23617379a912d8e4fefa399",
|
||||
"sha256:f0abbd234a588e572f880f99a094ac321217ff3eade6c0eca118ed6db8e2cf0a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.0"
|
||||
"version": "==4.10.0"
|
||||
},
|
||||
"tzdata": {
|
||||
"hashes": [
|
||||
"sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d",
|
||||
"sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.7"
|
||||
},
|
||||
"tzlocal": {
|
||||
"hashes": [
|
||||
"sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745",
|
||||
"sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
|
||||
"sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
|
||||
"extras": [
|
||||
"secure",
|
||||
"socks"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.8"
|
||||
"hashes": [
|
||||
"sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc",
|
||||
"sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==1.26.13"
|
||||
},
|
||||
"urllib3-secure-extra": {
|
||||
"hashes": [
|
||||
"sha256:ee9409cbfeb4b8609047be4c32fb4317870c602767e53fd8a41005ebe6a41dff",
|
||||
"sha256:f7adcb108b4d12a4b26b99eb60e265d087f435052a76aefa396b6ee85e9a6ef9"
|
||||
],
|
||||
"version": "==0.1.0"
|
||||
},
|
||||
"webdriver-manager": {
|
||||
"hashes": [
|
||||
"sha256:50a6e174106542f5335cacc387cec7ada26812babc1aeca61c208a1bab2ac2c5",
|
||||
"sha256:c6d81590aae6fc0fb10cf7dd20c8c1b9bb043501f9cf62c316a854a0de841e32"
|
||||
"sha256:821e0737415044d7ad2fc4c1b25fbe0c0de602c62c94c5a25e4a096cf10f792f",
|
||||
"sha256:9e115be96f04f4a912431766e7675679527f62867a3c73f4738901ea89e2a1e9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.4.2"
|
||||
"version": "==3.8.1"
|
||||
},
|
||||
"websocket-client": {
|
||||
"hashes": [
|
||||
"sha256:074e2ed575e7c822fc0940d31c3ac9bb2b1142c303eafcf3e304e6ce035522e8",
|
||||
"sha256:6278a75065395418283f887de7c3beafb3aa68dada5cacbe4b214e8d26da499b"
|
||||
"sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574",
|
||||
"sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.3.1"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.4.2"
|
||||
},
|
||||
"wsproto": {
|
||||
"hashes": [
|
||||
"sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065",
|
||||
"sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.2.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ Notify if configured planes have taken off or landed using Python with <a href="
|
|||
|
||||
### Background
|
||||
|
||||
I made this program so I could track Elon Musk's Jet and share with others of his whereabouts on Twitter. [](https://twitter.com/ElonJet) I have now Expanded and run multiple accounts for multiple planes, a list of the accounts here [plane-notify Twitter List](https://twitter.com/i/lists/1307414615316467715)
|
||||
I made this program so I could track Elon Musk's Jet and share his whereabouts with others on Twitter. [](https://twitter.com/ElonJet) I have now expanded and run multiple accounts for multiple planes, a list of the accounts can be found here [plane-notify Twitter List](https://twitter.com/i/lists/1307414615316467715)
|
||||
|
||||
### Contributing
|
||||
|
||||
I'm open to any help or suggestions, I realize there are many better ways to improve this program and better ways to get this program to work properly, Im only a noob. I'll accept pull requests. If you'd like to discuss join <https://JacksTech.net/Discord>
|
||||
I'm open to any help or suggestions, I realize there are many better ways to improve this program and better ways to get this program to work properly, I'm only a noob. I'll accept pull requests. If you'd like to discuss join <https://discord.gg/groundcontrol>
|
||||
|
||||
### [Algorithm](PseudoCode.md)
|
||||
|
||||
|
|
@ -67,6 +67,7 @@ cd plane-notify
|
|||
|
||||
### Configure main config file with keys and URLs (mainconf.ini) in the configs directory
|
||||
|
||||
- Copy `mainconf.ini.example` to `mainconf.ini` andCopy `plane1.ini.example` to `plane1.ini`. `plane1.ini` can change names as long as it ends with the ini extension
|
||||
- Edit them with nano or vi on the running machine or on your pc and transfer the config to where you will be running the bot
|
||||
- Pick between OpenSky and ADS-B Exchange
|
||||
- The OpenSky API is free for everyone but the data is not as good as ADS-B Exchange. The ADS-B Exchange API is not free and this program will not work for the Rapid API from ADS-B Exchange. It only works with the API that they give when you have a partnership with ADS-B Exchange. It is not cheap to get the ADS-B Exchange full API, Don't contact them unless you are ready to pay.
|
||||
|
|
@ -91,7 +92,7 @@ screen -R <name screen whatever you want>
|
|||
### Start Program
|
||||
|
||||
```bash
|
||||
pipenv run python __main__
|
||||
pipenv run python __main__.py
|
||||
```
|
||||
|
||||
## Using with Docker
|
||||
|
|
|
|||
73
__main__.py
73
__main__.py
|
|
@ -3,13 +3,19 @@ import time
|
|||
from colorama import Fore, Back, Style
|
||||
import platform
|
||||
import traceback
|
||||
import os
|
||||
if platform.system() == "Windows":
|
||||
from colorama import init
|
||||
init(convert=True)
|
||||
elif platform.system() == "Linux":
|
||||
if os.path.exists("/tmp/plane-notify"):
|
||||
import shutil
|
||||
shutil.rmtree("/tmp/plane-notify")
|
||||
os.makedirs("/tmp/plane-notify")
|
||||
os.makedirs("/tmp/plane-notify/chrome")
|
||||
from planeClass import Plane
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
import os
|
||||
import signal
|
||||
abspath = os.path.abspath(__file__)
|
||||
dname = os.path.dirname(abspath)
|
||||
|
|
@ -48,11 +54,13 @@ main_config.read('./configs/mainconf.ini')
|
|||
source = main_config.get('DATA', 'SOURCE')
|
||||
if main_config.getboolean('DISCORD', 'ENABLE'):
|
||||
from defDiscord import sendDis
|
||||
sendDis("Started", main_config)
|
||||
role_id = main_config.get('DISCORD', 'ROLE_ID') if main_config.has_option('DISCORD', 'ROLE_ID') and main_config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis("Started", main_config, role_id = main_config.get('DISCORD', 'ROLE_ID'))
|
||||
def service_exit(signum, frame):
|
||||
if main_config.getboolean('DISCORD', 'ENABLE'):
|
||||
from defDiscord import sendDis
|
||||
sendDis("Service Stop", main_config)
|
||||
role_id = main_config.get('DISCORD', 'ROLE_ID') if main_config.has_option('DISCORD', 'ROLE_ID') and main_config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis("Service Stop", main_config, role_id = role_id)
|
||||
raise SystemExit("Service Stop")
|
||||
signal.signal(signal.SIGTERM, service_exit)
|
||||
if os.path.isfile("lookup_route.py"):
|
||||
|
|
@ -68,7 +76,7 @@ try:
|
|||
print("Found the following configs")
|
||||
for dirpath, dirname, filename in os.walk("./configs"):
|
||||
for filename in [f for f in filename if f.endswith(".ini") and f != "mainconf.ini"]:
|
||||
if not "disabled" in dirpath:
|
||||
if "disabled" not in dirpath:
|
||||
print(os.path.join(dirpath, filename))
|
||||
plane_config = configparser.ConfigParser()
|
||||
plane_config.read((os.path.join(dirpath, filename)))
|
||||
|
|
@ -150,6 +158,54 @@ try:
|
|||
obj.run_empty()
|
||||
else:
|
||||
failed_count += 1
|
||||
elif source == "RpdADSBX":
|
||||
#ACAS data
|
||||
from defADSBX import pull_date_ras
|
||||
import ast
|
||||
today = datetime.utcnow()
|
||||
date = today.strftime("%Y/%m/%d")
|
||||
ras = pull_date_ras(date)
|
||||
sorted_ras = {}
|
||||
if ras is not None:
|
||||
#Testing RAs
|
||||
#if last_ra_count is not None:
|
||||
# with open('./testing/acastest.json') as f:
|
||||
# data = f.readlines()
|
||||
# ras += data
|
||||
ra_count = len(ras)
|
||||
if last_ra_count is not None and ra_count != last_ra_count:
|
||||
print(abs(ra_count - last_ra_count), "new Resolution Advisories")
|
||||
for ra_num, ra in enumerate(ras[last_ra_count:]):
|
||||
ra = ast.literal_eval(ra)
|
||||
if ra['hex'].upper() in planes.keys():
|
||||
if ra['hex'].upper() not in sorted_ras.keys():
|
||||
sorted_ras[ra['hex'].upper()] = [ra]
|
||||
else:
|
||||
sorted_ras[ra['hex'].upper()].append(ra)
|
||||
else:
|
||||
print("No new Resolution Advisories")
|
||||
last_ra_count = ra_count
|
||||
for key, obj in planes.items():
|
||||
if sorted_ras != {} and key in sorted_ras.keys():
|
||||
print(key, "has", len(sorted_ras[key]), "RAs")
|
||||
obj.check_new_ras(sorted_ras[key])
|
||||
obj.expire_ra_types()
|
||||
from defRpdADSBX import pull_rpdadsbx
|
||||
data_indexed = {}
|
||||
for icao in planes:
|
||||
plane = planes[icao]
|
||||
plane_info = pull_rpdadsbx(icao)
|
||||
if plane_info:
|
||||
if plane_info['ac']:
|
||||
data_indexed[icao.upper()] = plane_info['ac'][0]
|
||||
plane.run_adsbx_v2(data_indexed[icao.upper()])
|
||||
else:
|
||||
plane.run_empty()
|
||||
else:
|
||||
print(f"No data for icao {icao}. Skipping...")
|
||||
plane.run_empty()
|
||||
if not data_indexed:
|
||||
failed_count += 1
|
||||
elif source == "OPENS":
|
||||
from defOpenSky import pull_opensky
|
||||
planeData, failed = pull_opensky(planes)
|
||||
|
|
@ -184,7 +240,10 @@ try:
|
|||
footer = "-------- " + str(running_Count) + " -------- " + str(datetime_tz.strftime("%I:%M:%S %p")) + " ------------------------Elapsed Time- " + str(round(elapsed_calc_time, 3)) + " -------------------------------------"
|
||||
print (Back.GREEN + Fore.BLACK + footer[0:100] + Style.RESET_ALL)
|
||||
|
||||
sleep_sec = 30
|
||||
if main_config.has_section('SLEEP'):
|
||||
sleep_sec = int(main_config.get('SLEEP', 'SLEEPSEC'))
|
||||
else:
|
||||
sleep_sec = 30
|
||||
for i in range(sleep_sec,0,-1):
|
||||
if i < 10:
|
||||
i = " " + str(i)
|
||||
|
|
@ -211,5 +270,5 @@ except Exception as e:
|
|||
logging.error(e)
|
||||
logging.error(str(traceback.format_exc()))
|
||||
from defDiscord import sendDis
|
||||
sendDis(str("Error Exiting: " + str(e) + "Failed on " + key), main_config, "crash_latest.log")
|
||||
raise e
|
||||
sendDis(str("Error Exiting: " + str(e) + f"Failed on ({obj.config_path}) https://globe.adsbexchange.com/?icao={key} "), main_config, main_config.get('DISCORD', 'ROLE_ID'), "crash_latest.log")
|
||||
raise e
|
||||
|
|
|
|||
|
|
@ -0,0 +1,412 @@
|
|||
{
|
||||
"EA50": {
|
||||
"name": "Eclipse 550",
|
||||
"galph": 76,
|
||||
"category": "VLJ"
|
||||
},
|
||||
"LJ31": {
|
||||
"name": "Learjet 31",
|
||||
"galph": 202,
|
||||
"category": "Light"
|
||||
},
|
||||
"LJ40": {
|
||||
"name": "Learjet 40",
|
||||
"galph": 207,
|
||||
"category": "Light"
|
||||
},
|
||||
"PC24": {
|
||||
"name": "Pilatus PC-24",
|
||||
"galph": 154,
|
||||
"category": "Light"
|
||||
},
|
||||
"LJ45": {
|
||||
"name": "Learjet 45",
|
||||
"galph": 205,
|
||||
"category": "Super Light"
|
||||
},
|
||||
"LJ70": {
|
||||
"name": "Learjet 70",
|
||||
"galph": 198,
|
||||
"category": "Super Light"
|
||||
},
|
||||
"LJ75": {
|
||||
"name": "Learjet 75",
|
||||
"galph": 199,
|
||||
"category": "Super Light"
|
||||
},
|
||||
"G150": {
|
||||
"name": "Gulfstream G150",
|
||||
"galph": 228,
|
||||
"category": "Midsize"
|
||||
},
|
||||
"LJ60": {
|
||||
"name": "Learjet 60",
|
||||
"galph": 239,
|
||||
"category": "Midsize"
|
||||
},
|
||||
"GALX": {
|
||||
"name": "Gulfstream G200",
|
||||
"galph": 278,
|
||||
"category": "Super Midsize"
|
||||
},
|
||||
"G280": {
|
||||
"name": "Gulfstream G280",
|
||||
"galph": 297,
|
||||
"category": "Super Midsize"
|
||||
},
|
||||
"GLF5": {
|
||||
"name": "Gulfstream G500",
|
||||
"galph": 447,
|
||||
"category": "Large"
|
||||
},
|
||||
"GLF6": {
|
||||
"name": "Gulfstream G650",
|
||||
"galph": 503,
|
||||
"category": "Ultra Long Range"
|
||||
},
|
||||
"PC12": {
|
||||
"name": "Pilatus PC-12",
|
||||
"galph": 66,
|
||||
"category": "Turboprop Aircraft"
|
||||
},
|
||||
"GLEX": {
|
||||
"name": "Global",
|
||||
"galph": 500,
|
||||
"category": "Ultra Long Range"
|
||||
},
|
||||
"CL30": {
|
||||
"name": "Challenger 300",
|
||||
"galph": 295,
|
||||
"category": "Super Midsize"
|
||||
},
|
||||
"B742": {
|
||||
"name": "Boeing 747-200",
|
||||
"galph": 3830,
|
||||
"category": "Large"
|
||||
},
|
||||
"T38": {
|
||||
"name": "T-38 Talon",
|
||||
"galph": 375,
|
||||
"category": "Fighter"
|
||||
},
|
||||
"WB57": {
|
||||
"name": "Martin B-57 Canberra",
|
||||
"galph": 531,
|
||||
"category": "Twinjet Tactical Bomber and Reconnaissance"
|
||||
},
|
||||
"B74S": {
|
||||
"name": "747 SP",
|
||||
"galph": 2289,
|
||||
"category": "Large"
|
||||
},
|
||||
"B752": {
|
||||
"name": "757 200",
|
||||
"galph": 877,
|
||||
"category": "Large"
|
||||
},
|
||||
"B738": {
|
||||
"name": "737 800",
|
||||
"galph": 832,
|
||||
"category": "Medium"
|
||||
},
|
||||
"B737": {
|
||||
"name": "737 700",
|
||||
"galph": 796,
|
||||
"category": "Medium"
|
||||
},
|
||||
"A320": {
|
||||
"name": "A320",
|
||||
"galph": 800,
|
||||
"category": "Medium"
|
||||
},
|
||||
"P3": {
|
||||
"name": "Lockheed Orion P3",
|
||||
"galph": 671,
|
||||
"category": "Turboprop"
|
||||
},
|
||||
"C750": {
|
||||
"name": "Cessna 750 Citation X",
|
||||
"galph": 347,
|
||||
"category": "Small Private Jet"
|
||||
},
|
||||
"FA7X": {
|
||||
"name": "Dassult Falcon 7X",
|
||||
"galph": 380,
|
||||
"category": "Small Private Jet"
|
||||
},
|
||||
"F900": {
|
||||
"name": "Dassult Falcon 900",
|
||||
"galph": 347,
|
||||
"category": "Small Private Jet"
|
||||
},
|
||||
"H25B": {
|
||||
"name": "Hawker 750/850",
|
||||
"galph": 270,
|
||||
"category": "Small Private Jet"
|
||||
},
|
||||
"C680": {
|
||||
"name": "Cessna 680 Citation",
|
||||
"galph": 247,
|
||||
"category": "Small Private Jet"
|
||||
},
|
||||
"GLF3": {
|
||||
"name": "Gulfstream 3",
|
||||
"galph": 568,
|
||||
"category": "Heavy Private Jet"
|
||||
},
|
||||
"GLF4": {
|
||||
"name": "Gulfstream 4",
|
||||
"galph": 479,
|
||||
"category": "Heavy Private Jet"
|
||||
},
|
||||
"CL60": {
|
||||
"name": "Bombardier CL-600 Challenge",
|
||||
"galph": 262,
|
||||
"category": "Mid-size Private Jet"
|
||||
},
|
||||
"A139": {
|
||||
"name": "Agusta-Bell AW139",
|
||||
"galph": 150,
|
||||
"category": "Medium Utility Helicopter"
|
||||
},
|
||||
"GL5T": {
|
||||
"name": "Global 5000",
|
||||
"galph": 455,
|
||||
"category": "Heavy Private Jet"
|
||||
},
|
||||
"GA6C": {
|
||||
"name": "Gulfstream G600",
|
||||
"galph": 458,
|
||||
"category": "Heavy Private Jet"
|
||||
},
|
||||
"A337": {
|
||||
"name": "Airbus Beluga XL",
|
||||
"galph": 1800,
|
||||
"category": "Large Transport Aircraft"
|
||||
},
|
||||
"A3ST": {
|
||||
"name": "Airbus Beluga",
|
||||
"galph": 1260,
|
||||
"category": "Large Transport Aircraft"
|
||||
},
|
||||
"F2TH": {
|
||||
"name": "Dassault Falcon 2000",
|
||||
"galph": 245,
|
||||
"category": "Medium Private Jet"
|
||||
},
|
||||
"GA5C": {
|
||||
"name": "Gulfstream G500",
|
||||
"galph": 402,
|
||||
"category": "Large Private Jet"
|
||||
},
|
||||
"C130": {
|
||||
"name": "Lockheed C130",
|
||||
"galph": 740,
|
||||
"category": "Medium Cargo"
|
||||
},
|
||||
"B762": {
|
||||
"name": "Boeing 767 200",
|
||||
"galph": 1722,
|
||||
"category": "Wide-body"
|
||||
},
|
||||
"B772": {
|
||||
"name": "Boeing 777 200",
|
||||
"galph": 2300,
|
||||
"category": "Wide-body"
|
||||
},
|
||||
"SLCH": {
|
||||
"name": "Stratolaunch",
|
||||
"galph": 2396,
|
||||
"category": "Special"
|
||||
},
|
||||
"P51": {
|
||||
"name": "P51 Mustang",
|
||||
"galph": 65,
|
||||
"category": "Fighter"
|
||||
},
|
||||
"HDJT": {
|
||||
"name": "Honda Jet",
|
||||
"galph": 90,
|
||||
"category": "Light Jet"
|
||||
},
|
||||
"B744": {
|
||||
"name": "Boeing 747-400",
|
||||
"galph": 3700,
|
||||
"category": "Heavy Airliner"
|
||||
},
|
||||
"E190": {
|
||||
"name": "Embrar E190",
|
||||
"galph": 469,
|
||||
"category": "Heavy Jet"
|
||||
},
|
||||
"FA50": {
|
||||
"name": "Falcon 50",
|
||||
"galph": 229,
|
||||
"category": "Heavy Jet"
|
||||
},
|
||||
"GL7T": {
|
||||
"name": "Global 7000",
|
||||
"galph": 460,
|
||||
"category": "Heavy Jet"
|
||||
},
|
||||
"GL6T": {
|
||||
"name": "",
|
||||
"galph": 455.0,
|
||||
"category": ""
|
||||
},
|
||||
"C68A": {
|
||||
"name": "",
|
||||
"galph": 212.0,
|
||||
"category": ""
|
||||
},
|
||||
"C56X": {
|
||||
"name": "",
|
||||
"galph": 217.0,
|
||||
"category": ""
|
||||
},
|
||||
"B763": {
|
||||
"name": "",
|
||||
"galph": 1320.0,
|
||||
"category": ""
|
||||
},
|
||||
"A310": {
|
||||
"name": "",
|
||||
"galph": 1189.0,
|
||||
"category": ""
|
||||
},
|
||||
"A330": {
|
||||
"name": "",
|
||||
"galph": 1505.0,
|
||||
"category": ""
|
||||
},
|
||||
"A380": {
|
||||
"name": "",
|
||||
"galph": 4062.0,
|
||||
"category": ""
|
||||
},
|
||||
"E170": {
|
||||
"name": "",
|
||||
"galph": 469.0,
|
||||
"category": ""
|
||||
},
|
||||
"DC87": {
|
||||
"name": "",
|
||||
"galph": 1250.0,
|
||||
"category": ""
|
||||
},
|
||||
"SGUP": {
|
||||
"name": "",
|
||||
"galph": 1156.0,
|
||||
"category": ""
|
||||
},
|
||||
"WHK2": {
|
||||
"name": "",
|
||||
"galph": 500.0,
|
||||
"category": ""
|
||||
},
|
||||
"B350": {
|
||||
"name": "",
|
||||
"galph": 122.0,
|
||||
"category": ""
|
||||
},
|
||||
"BE30": {
|
||||
"name": "",
|
||||
"galph": 121.0,
|
||||
"category": ""
|
||||
},
|
||||
"FA8X": {
|
||||
"name": "",
|
||||
"galph": 380.0,
|
||||
"category": ""
|
||||
},
|
||||
"E550": {
|
||||
"name": "",
|
||||
"galph": 280.0,
|
||||
"category": ""
|
||||
},
|
||||
"E55P": {
|
||||
"name": "",
|
||||
"galph": 166.0,
|
||||
"category": ""
|
||||
},
|
||||
"A332": {
|
||||
"name": "",
|
||||
"galph": 1480,
|
||||
"category": ""
|
||||
},
|
||||
"GA7C": {
|
||||
"name": "",
|
||||
"galph": 382.0,
|
||||
"category": ""
|
||||
},
|
||||
"FA6X": {
|
||||
"name": "",
|
||||
"galph": 419.0,
|
||||
"category": ""
|
||||
},
|
||||
"B3XM": {
|
||||
"name": "",
|
||||
"galph": 716.0,
|
||||
"category": ""
|
||||
},
|
||||
"B779": {
|
||||
"name": "",
|
||||
"galph": 2250.0,
|
||||
"category": ""
|
||||
},
|
||||
"BE22": {
|
||||
"name": "",
|
||||
"galph": 60.0,
|
||||
"category": ""
|
||||
},
|
||||
"C560": {
|
||||
"name": "",
|
||||
"galph": 182.0,
|
||||
"category": ""
|
||||
},
|
||||
"E145": {
|
||||
"name": "",
|
||||
"galph": 284.0,
|
||||
"category": ""
|
||||
},
|
||||
"C25C": {
|
||||
"name": "",
|
||||
"galph": 110.0,
|
||||
"category": ""
|
||||
},
|
||||
"C25B": {
|
||||
"name": "",
|
||||
"galph": 110.0,
|
||||
"category": ""
|
||||
},
|
||||
"C441": {
|
||||
"name": "",
|
||||
"galph": 57.0,
|
||||
"category": ""
|
||||
},
|
||||
"E50P": {
|
||||
"name": "",
|
||||
"galph": 109.0,
|
||||
"category": ""
|
||||
},
|
||||
"CRJ2": {
|
||||
"name": "",
|
||||
"galph": 325.0,
|
||||
"category": ""
|
||||
},
|
||||
"CRJ7": {
|
||||
"name": "",
|
||||
"galph": 444.0,
|
||||
"category": ""
|
||||
},
|
||||
"BE40": {
|
||||
"name": "",
|
||||
"galph": 220.0,
|
||||
"category": ""
|
||||
},
|
||||
"C700": {
|
||||
"name": "",
|
||||
"galph": 288.0,
|
||||
"category": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ def calculate_cardinal(d):
|
|||
dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
|
||||
ix = int(round(d / (360. / len(dirs))))
|
||||
card = dirs[ix % len(dirs)]
|
||||
print(card)
|
||||
return card
|
||||
def calculate_deg_change(new_heading, original_heading):
|
||||
"""Calculates change between two headings, returns negative degree if change is left, positive if right"""
|
||||
|
|
@ -29,6 +28,7 @@ def calculate_deg_change(new_heading, original_heading):
|
|||
track_change = normal
|
||||
if direction == "left":
|
||||
track_change *= -1
|
||||
track_change = round(track_change, 2)
|
||||
print(f"Track change of {track_change}° which is {direction}")
|
||||
return track_change
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
#Source to pull data from
|
||||
#SHOULD BE ADSBX which is ADS-B Exchange or OPENS which is OpenSky
|
||||
#By default configured with OpenSky which anyone can use without a login
|
||||
#ADS-B Exchange has better data but is not avalible unless you pay (see: https://www.adsbexchange.com/data/ )
|
||||
SOURCE = OPENS
|
||||
#ADS-B Exchange has better data but is not available unless you feed their network or pay.
|
||||
SOURCE = RpdADSBX
|
||||
#Default amount of time after data loss to trigger a landing when under 10k ft
|
||||
DATA_LOSS_MINS = 5
|
||||
#Failover from one source to the other, only enable if you have both sources setup.
|
||||
|
|
@ -20,7 +20,7 @@ API_VERSION = 1
|
|||
#ADSBX API Proxy, https://gitlab.com/jjwiseman/adsbx-api-proxy, v2 input, v1 or v2 output from proxy
|
||||
ENABLE_PROXY = FALSE
|
||||
#Full URL http://host:port
|
||||
PROXY_HOST =
|
||||
PROXY_HOST =
|
||||
|
||||
#OpenSky https://opensky-network.org/apidoc/index.html
|
||||
#When using without your own login user and pass should be None
|
||||
|
|
@ -28,12 +28,37 @@ PROXY_HOST =
|
|||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
#ADS-B Exchange on RapidAPI https://rapidapi.com/adsbx/api/adsbexchange-com1/
|
||||
[RpdADSBX]
|
||||
API_KEY = none
|
||||
API_VERSION = 2
|
||||
|
||||
#Define the delay interval in seconds between each data request. This is useful if you have limited requests in the API.
|
||||
[SLEEP]
|
||||
SLEEPSEC = 60
|
||||
|
||||
[GOOGLE]
|
||||
#API KEY for Google Static Maps only if you using this on any of the planes.
|
||||
API_KEY = googleapikey
|
||||
|
||||
#Used for failover messages and program exits notifcation
|
||||
#Used for failover messages and program exits notification
|
||||
[DISCORD]
|
||||
ENABLE = FALSE
|
||||
USERNAME = usernamehere
|
||||
URL = webhookurl
|
||||
ROLE_ID =
|
||||
|
||||
[TFRS]
|
||||
URL = http://127.0.0.1:5000/detailed_all
|
||||
ENABLE = False
|
||||
|
||||
[TWITTER]
|
||||
#GLOBAL TWITTER CONSUMER KEY AND CONSUMERS SECRET ONLY NEED TO BE HERE NOW
|
||||
ENABLE = False
|
||||
CONSUMER_KEY = ck
|
||||
CONSUMER_SECRET = cs
|
||||
|
||||
[MAP]
|
||||
#Map to create from Google Static Maps or screenshot global tar1090 from globe.adsbexchange.com
|
||||
#Enter GOOGLESTATICMAP or ADSBX
|
||||
OPTION = ADSBX
|
||||
|
|
@ -2,8 +2,15 @@
|
|||
#Plane to track, based off ICAO or ICAO24 which is the unique transponder address of a plane.
|
||||
ICAO = icaohere
|
||||
|
||||
#Optional Per Plane Override
|
||||
#DATA_LOSS_MINS = 20
|
||||
|
||||
#Optional Per Plane Overrides
|
||||
; OVERRIDE_REG=
|
||||
; OVERRIDE_ICAO_TYPE=
|
||||
; OVERRIDE_TYPELONG =
|
||||
; OVERRIDE_OWNER =
|
||||
; DATA_LOSS_MINS = 20
|
||||
; CONCEAL_AC_ID = True
|
||||
; CONCEAL_PIA = False
|
||||
|
||||
[MAP]
|
||||
#Map to create from Google Static Maps or screenshot global tar1090 from globe.adsbexchange.com
|
||||
|
|
@ -21,17 +28,9 @@ TYPES = [small_airport, medium_airport, large_airport]
|
|||
[TWITTER]
|
||||
ENABLE = FALSE
|
||||
TITLE =
|
||||
CONSUMER_KEY = ckhere
|
||||
CONSUMER_SECRET = cshere
|
||||
ACCESS_TOKEN = athere
|
||||
ACCESS_TOKEN_SECRET = atshere
|
||||
|
||||
[PUSHBULLET]
|
||||
ENABLE = FALSE
|
||||
TITLE = Title Of Pushbullet message
|
||||
API_KEY = apikey
|
||||
CHANNEL_TAG = channeltag
|
||||
|
||||
[DISCORD]
|
||||
ENABLE = FALSE
|
||||
#WEBHOOK URL https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
|
||||
|
|
@ -41,10 +40,15 @@ ROLE_ID =
|
|||
Title =
|
||||
USERNAME = plane-notify
|
||||
|
||||
[META]
|
||||
ENABLE = False
|
||||
FB_PAGE_ID =
|
||||
IG_USER_ID =
|
||||
ACCESS_TOKEN =
|
||||
|
||||
|
||||
[TELEGRAM]
|
||||
ENABLE = FALSE
|
||||
TITLE = Title Of Telegram message
|
||||
ROOM_ID = -100xxxxxxxxxx
|
||||
BOT_TOKEN = xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
=======
|
||||
|
||||
BOT_TOKEN = xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
|
@ -1,45 +1,45 @@
|
|||
import csv
|
||||
import math
|
||||
|
||||
|
||||
def add_airport_region(airport_dict):
|
||||
# Get full region/state name from iso region name
|
||||
with open('./dependencies/regions.csv', 'r', encoding='utf-8') as regions_csv:
|
||||
regions_csv = csv.DictReader(filter(lambda row: row[0] != '#', regions_csv))
|
||||
for region in regions_csv:
|
||||
if region['code'] == airport_dict['iso_region']:
|
||||
airport_dict['region'] = region['name']
|
||||
return airport_dict
|
||||
|
||||
|
||||
#Get full region/state name from iso region name
|
||||
with open('./dependencies/regions.csv', 'r', encoding='utf-8') as regions_csv:
|
||||
regions_csv = csv.DictReader(filter(lambda row: row[0]!='#', regions_csv))
|
||||
for region in regions_csv:
|
||||
if region['code'] == airport_dict['iso_region']:
|
||||
airport_dict['region'] = region['name']
|
||||
return airport_dict
|
||||
def getClosestAirport(latitude, longitude, allowed_types):
|
||||
from geopy.distance import geodesic
|
||||
plane = (latitude, longitude)
|
||||
closest_airport_dict = dict()
|
||||
with open('./dependencies/airports.csv', 'r', encoding='utf-8') as airport_csv:
|
||||
airport_csv_reader = csv.DictReader(filter(lambda row: row[0] != '#', airport_csv))
|
||||
for airport in airport_csv_reader:
|
||||
if airport['type'] in allowed_types:
|
||||
airport_coord = float(airport['latitude_deg']), float(airport['longitude_deg'])
|
||||
airport_dist = float((geodesic(plane, airport_coord).mi))
|
||||
if not closest_airport_dict or airport_dist < closest_airport_dist:
|
||||
closest_airport_dict = airport
|
||||
closest_airport_dist = airport_dist
|
||||
closest_airport_dict['distance_mi'] = closest_airport_dist
|
||||
# Convert indent key to icao key as its labeled icao in other places not ident
|
||||
closest_airport_dict['icao'] = closest_airport_dict.pop('gps_code')
|
||||
closest_airport_dict = add_airport_region(closest_airport_dict)
|
||||
return closest_airport_dict
|
||||
|
||||
|
||||
from geopy.distance import geodesic
|
||||
plane = (latitude, longitude)
|
||||
with open('./dependencies/airports.csv', 'r', encoding='utf-8') as airport_csv:
|
||||
airport_csv_reader = csv.DictReader(filter(lambda row: row[0]!='#', airport_csv))
|
||||
for airport in airport_csv_reader:
|
||||
if airport['type'] in allowed_types:
|
||||
airport_coord = float(airport['latitude_deg']), float(airport['longitude_deg'])
|
||||
airport_dist = float((geodesic(plane, airport_coord).mi))
|
||||
if "closest_airport_dict" not in locals():
|
||||
closest_airport_dict = airport
|
||||
closest_airport_dist = airport_dist
|
||||
elif airport_dist < closest_airport_dist:
|
||||
closest_airport_dict = airport
|
||||
closest_airport_dist = airport_dist
|
||||
closest_airport_dict['distance_mi'] = closest_airport_dist
|
||||
#Convert indent key to icao key as its labeled icao in other places not ident
|
||||
closest_airport_dict['icao'] = closest_airport_dict.pop('gps_code')
|
||||
closest_airport_dict = add_airport_region(closest_airport_dict)
|
||||
return closest_airport_dict
|
||||
def get_airport_by_icao(icao):
|
||||
with open('./dependencies/airports.csv', 'r', encoding='utf-8') as airport_csv:
|
||||
airport_csv_reader = csv.DictReader(filter(lambda row: row[0] != '#', airport_csv))
|
||||
for airport in airport_csv_reader:
|
||||
if airport['gps_code'] == icao:
|
||||
matching_airport = airport
|
||||
# Convert indent key to icao key as its labeled icao in other places not ident
|
||||
matching_airport['icao'] = matching_airport.pop('gps_code')
|
||||
break
|
||||
matching_airport = add_airport_region(matching_airport)
|
||||
return matching_airport
|
||||
with open('./dependencies/airports.csv', 'r', encoding='utf-8') as airport_csv:
|
||||
matching_airport = None
|
||||
airport_csv_reader = csv.DictReader(filter(lambda row: row[0]!='#', airport_csv))
|
||||
for airport in airport_csv_reader:
|
||||
if airport['gps_code'] == icao:
|
||||
matching_airport = airport
|
||||
#Convert indent key to icao key as its labeled icao in other places not ident
|
||||
matching_airport['icao'] = matching_airport.pop('gps_code')
|
||||
break
|
||||
if matching_airport:
|
||||
matching_airport = add_airport_region(matching_airport)
|
||||
return matching_airport
|
||||
else:
|
||||
return None
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
def sendDis(message, config, file_name = None, role_id = None):
|
||||
def sendDis(message, config, role_id = None, *file_names):
|
||||
import requests
|
||||
from discord_webhook import DiscordWebhook
|
||||
if role_id != None:
|
||||
message += f" <@&{role_id}>"
|
||||
webhook = DiscordWebhook(url=config.get('DISCORD', 'URL'), content=message[0:1999], username=config.get('DISCORD', 'USERNAME'))
|
||||
if file_name != None:
|
||||
with open(file_name, "rb") as f:
|
||||
webhook.add_file(file=f.read(), filename=file_name)
|
||||
|
||||
if file_names != []:
|
||||
for file_name in file_names:
|
||||
with open(file_name, "rb") as f:
|
||||
webhook.add_file(file=f.read(), filename=file_name)
|
||||
try:
|
||||
webhook.execute()
|
||||
except requests.exceptions.RequestException:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ def pull_opensky(planes):
|
|||
main_config.read('./configs/mainconf.ini')
|
||||
from opensky_api import OpenSkyApi
|
||||
planeData = None
|
||||
opens_api = OpenSkyApi(username= None if main_config.get('OPENSKY', 'USERNAME').upper() == "NONE" else main_config.get('OPENSKY', 'USERNAME'), password= None if main_config.get('OPENSKY', 'PASSWORD').upper() == "NONE" else main_config.get('OPENSKY', 'PASSWORD').upper())
|
||||
opens_api = OpenSkyApi(
|
||||
username= None if main_config.get('OPENSKY', 'USERNAME').upper() == "NONE" else main_config.get('OPENSKY', 'USERNAME'),
|
||||
password= None if main_config.get('OPENSKY', 'PASSWORD').upper() == "NONE" else main_config.get('OPENSKY', 'PASSWORD'))
|
||||
failed = False
|
||||
icao_array = []
|
||||
for key in planes.keys():
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import requests
|
||||
import configparser
|
||||
from datetime import datetime
|
||||
|
||||
main_config = configparser.ConfigParser()
|
||||
main_config.read('./configs/mainconf.ini')
|
||||
api_version = main_config.get('RpdADSBX', 'API_VERSION')
|
||||
|
||||
def pull_rpdadsbx(planes):
|
||||
api_version = int(main_config.get('RpdADSBX', 'API_VERSION'))
|
||||
if api_version != 2:
|
||||
raise ValueError("Bad RapidAPI ADSBX API Version")
|
||||
url = "https://adsbexchange-com1.p.rapidapi.com/v2/icao/" + planes + "/"
|
||||
headers = {
|
||||
"X-RapidAPI-Host": "adsbexchange-com1.p.rapidapi.com",
|
||||
"X-RapidAPI-Key": main_config.get('RpdADSBX', 'API_KEY')
|
||||
}
|
||||
try:
|
||||
response = requests.get(url, headers = headers, timeout=30)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if "msg" in data.keys() and data['msg'] != "No error":
|
||||
raise ValueError("Error from ADSBX: msg = ", data['msg'])
|
||||
if "ctime" in data.keys():
|
||||
data_ctime = float(data['ctime']) / 1000.0
|
||||
print("Data ctime:",datetime.utcfromtimestamp(data_ctime))
|
||||
if "now" in data.keys():
|
||||
data_now = float(data['now']) / 1000.0
|
||||
print("Data now time:",datetime.utcfromtimestamp(data_now))
|
||||
print("Current UTC:", datetime.utcnow())
|
||||
return data
|
||||
except Exception as e:
|
||||
print('Error calling RapidAPI', e)
|
||||
return None
|
||||
117
defSS.py
117
defSS.py
|
|
@ -1,29 +1,46 @@
|
|||
import json
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from webdriver_manager.chrome import ChromeDriverManager
|
||||
import time
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.common.by import By
|
||||
def get_adsbx_screenshot(file_path, url_params, enable_labels=False, enable_track_labels=False):
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
def blur_elements_by_id(browser, element_ids):
|
||||
for element in element_ids:
|
||||
try:
|
||||
element = browser.find_element(By.ID, element)
|
||||
browser.execute_script("arguments[0].style.filter = 'blur(7px)';", element)
|
||||
except NoSuchElementException:
|
||||
print("Issue finding:", element, "on page")
|
||||
def get_adsbx_screenshot(file_path, url_params, enable_labels=False, enable_track_labels=False, overrides={}, conceal_ac_id=False, conceal_pia=False):
|
||||
import os
|
||||
import platform
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
chrome_options.headless = True
|
||||
chrome_options.add_argument('window-size=800,800')
|
||||
chrome_options.add_argument('ignore-certificate-errors')
|
||||
chrome_options.add_argument("--enable-logging --v=1")
|
||||
import os
|
||||
import platform
|
||||
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
|
||||
if platform.system() == "Linux":
|
||||
chrome_options.add_argument('crash-dumps-dir=/tmp/plane-notify/chrome')
|
||||
|
||||
#Plane images issue loading when in headless setting agent fixes.
|
||||
chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36")
|
||||
if platform.system() == "Linux" and os.geteuid()==0:
|
||||
chrome_options.add_argument('--no-sandbox') # required when running as root user. otherwise you would get no sandbox errors.
|
||||
browser = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
|
||||
browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
|
||||
url = f"https://globe.adsbexchange.com/?{url_params}"
|
||||
print(f"Getting Screenshot of {url}")
|
||||
browser.set_page_load_timeout(80)
|
||||
browser.get(url)
|
||||
WebDriverWait(browser, 40).until(lambda d: d.execute_script("return jQuery.active == 0"))
|
||||
remove_id_elements = ["show_trace", "credits", 'infoblock_close', 'selected_photo_link', "history_collapse"]
|
||||
for element in remove_id_elements:
|
||||
try:
|
||||
element = browser.find_element_by_id(element)
|
||||
element = browser.find_element(By.ID, element)
|
||||
browser.execute_script("""var element = arguments[0]; element.parentNode.removeChild(element); """, element)
|
||||
except:
|
||||
print("issue removing", element, "from map")
|
||||
print("Issue finding:", element, "on page")
|
||||
#Remove watermark on data
|
||||
try:
|
||||
browser.execute_script("document.getElementById('selected_infoblock').className = 'none';")
|
||||
|
|
@ -36,42 +53,72 @@ def get_adsbx_screenshot(file_path, url_params, enable_labels=False, enable_trac
|
|||
print("Couldn't disable sidebar on map")
|
||||
#Remove Google Ads
|
||||
try:
|
||||
element = browser.find_element_by_xpath("//*[contains(@id, 'FIOnDemandWrapper_')]")
|
||||
element = browser.find_element(By.XPATH, "//*[contains(@id, 'FIOnDemandWrapper_')]")
|
||||
browser.execute_script("""var element = arguments[0]; element.parentNode.removeChild(element); """, element)
|
||||
except:
|
||||
print("Couldn't remove Google Ads")
|
||||
#Remove share
|
||||
#Remove Copy Link
|
||||
try:
|
||||
element = browser.find_element_by_xpath("//*[contains(text(), 'Share')]")
|
||||
element = browser.find_element(By.XPATH, "//*[@id='selected_icao']/span[2]/a")
|
||||
browser.execute_script("""var element = arguments[0]; element.parentNode.removeChild(element); """, element)
|
||||
except:
|
||||
print("Couldn't remove share button from map")
|
||||
except Exception as e:
|
||||
print("Couldn't remove copy link button from map", e)
|
||||
#browser.execute_script("toggleFollow()")
|
||||
if conceal_pia or conceal_ac_id:
|
||||
blur_elements_by_id(browser, ["selected_callsign", "selected_icao", "selected_squawk1"])
|
||||
if conceal_ac_id:
|
||||
blur_elements_by_id(browser, ["selected_registration", "selected_country", "selected_dbFlags", "selected_ownop", "selected_typelong", "selected_icaotype", "airplanePhoto", "silhouette", "copyrightInfo"])
|
||||
if enable_labels:
|
||||
browser.find_element_by_tag_name('body').send_keys('l')
|
||||
browser.find_element(By.TAG_NAME, 'body').send_keys('l')
|
||||
if enable_track_labels:
|
||||
browser.find_element_by_tag_name('body').send_keys('k')
|
||||
WebDriverWait(browser, 40).until(lambda d: d.execute_script("return jQuery.active == 0"))
|
||||
try:
|
||||
photo_box = browser.find_element_by_id("silhouette")
|
||||
except:
|
||||
pass
|
||||
browser.find_element(By.TAG_NAME, 'body').send_keys('k')
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
time.sleep(15)
|
||||
|
||||
if 'reg' in overrides.keys():
|
||||
element = browser.find_element(By.ID, "selected_registration")
|
||||
browser.execute_script(f"arguments[0].innerText = '* {overrides['reg']}'", element)
|
||||
reg = overrides['reg']
|
||||
else:
|
||||
import requests, json
|
||||
photo_list = json.loads(requests.get("https://raw.githubusercontent.com/Jxck-S/aircraft-photos/main/photo-list.json").text)
|
||||
if "icao" in url_params:
|
||||
import re
|
||||
|
||||
icao = re.search('icao=(.+?)&', url_params).group(1).lower()
|
||||
print(icao)
|
||||
if icao in photo_list.keys():
|
||||
browser.execute_script("arguments[0].id = 'airplanePhoto';", photo_box)
|
||||
browser.execute_script(f"arguments[0].src = 'https://raw.githubusercontent.com/Jxck-S/aircraft-photos/main/images/{photo_list[icao]['reg']}.jpg';", photo_box)
|
||||
copyright = browser.find_element_by_id("copyrightInfo")
|
||||
browser.execute_script("arguments[0].id = 'copyrightInfoFreeze';", copyright)
|
||||
browser.execute_script("$('#copyrightInfoFreeze').css('font-size', '12px');")
|
||||
browser.execute_script(f"arguments[0].appendChild(document.createTextNode('Image © {photo_list[icao]['photographer']}'))", copyright)
|
||||
|
||||
try:
|
||||
reg = browser.find_element(By.ID, "selected_registration").get_attribute("innerHTML")
|
||||
print("Reg from tar1090 is", reg)
|
||||
except Exception as e:
|
||||
print("Couldn't find reg in tar1090", e)
|
||||
reg = None
|
||||
if reg is not None:
|
||||
try:
|
||||
try:
|
||||
photo_box = browser.find_element(By.ID, "silhouette")
|
||||
except NoSuchElementException:
|
||||
photo_box = browser.find_element(By.ID, "airplanePhoto")
|
||||
finally:
|
||||
import requests, json
|
||||
photo_list = json.loads(requests.get("https://raw.githubusercontent.com/Jxck-S/aircraft-photos/main/photo-list.json", timeout=20).text)
|
||||
if reg in photo_list.keys():
|
||||
browser.execute_script("arguments[0].id = 'airplanePhoto';", photo_box)
|
||||
browser.execute_script("arguments[0].removeAttribute('width')", photo_box)
|
||||
browser.execute_script("arguments[0].style.width = '200px';", photo_box)
|
||||
browser.execute_script("arguments[0].style.float = 'left';", photo_box)
|
||||
browser.execute_script(f"arguments[0].src = 'https://raw.githubusercontent.com/Jxck-S/aircraft-photos/main/images/{reg}.jpg';", photo_box)
|
||||
image_copy_right = browser.find_element(By.ID, "copyrightInfo")
|
||||
browser.execute_cdp_cmd('Emulation.setScriptExecutionDisabled', {'value': True})
|
||||
copy_right_children = image_copy_right.find_elements(By.XPATH, "*")
|
||||
if len(copy_right_children) > 0:
|
||||
browser.execute_script(f"arguments[0].innerText = 'Image © {photo_list[reg]['photographer']}'", copy_right_children[0])
|
||||
else:
|
||||
browser.execute_script(f"arguments[0].appendChild(document.createTextNode('Image © {photo_list[reg]['photographer']}'))", image_copy_right)
|
||||
except Exception as e:
|
||||
print("Error on changing photo", e)
|
||||
if 'type' in overrides.keys():
|
||||
element = browser.find_element(By.ID, "selected_icaotype")
|
||||
browser.execute_script(f"arguments[0].innerText = '* {overrides['type']}'", element)
|
||||
if 'typelong' in overrides.keys():
|
||||
element = browser.find_element(By.ID, "selected_typelong")
|
||||
browser.execute_script(f"arguments[0].innerText = '* {overrides['typelong']}'", element)
|
||||
if 'ownop' in overrides.keys():
|
||||
element = browser.find_element(By.ID, "selected_ownop")
|
||||
browser.execute_script(f"arguments[0].innerText = '* {overrides['ownop']}'", element)
|
||||
time.sleep(5)
|
||||
browser.save_screenshot(file_path)
|
||||
browser.quit()
|
||||
|
|
@ -82,4 +129,4 @@ def generate_adsbx_screenshot_time_params(timestamp):
|
|||
print(timestamp_dt)
|
||||
start_time = timestamp_dt - timedelta(minutes=1)
|
||||
time_params = "&showTrace=" + timestamp_dt.strftime("%Y-%m-%d") + "&startTime=" + start_time.strftime("%H:%M:%S") + "&endTime=" + timestamp_dt.strftime("%H:%M:%S")
|
||||
return time_params
|
||||
return time_params
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ def sendTeleg(photo, message, config):
|
|||
print('Telegram module couldn\'t find an image to send.')
|
||||
break
|
||||
elif str(err) == 'Media_caption_too_long':
|
||||
print('Telegram image caption lenght exceeds 1024 characters. Message not send.')
|
||||
print('Telegram image caption length exceeds 1024 characters. Message not send.')
|
||||
break
|
||||
else:
|
||||
print('[X] Unknown Telegram error. Message not sent.')
|
||||
break
|
||||
else:
|
||||
print("Telegram message successfully sent.")
|
||||
return sent
|
||||
return sent
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
# Authenticate to Twitter
|
||||
def tweepysetup(config):
|
||||
import tweepy
|
||||
#DOCU
|
||||
#https://realpython.com/twitter-bot-python-tweepy/
|
||||
auth = tweepy.OAuthHandler(config.get('TWITTER', 'CONSUMER_KEY'), config.get('TWITTER', 'CONSUMER_SECRET'))
|
||||
auth.set_access_token(config.get('TWITTER', 'ACCESS_TOKEN'), config.get('TWITTER', 'ACCESS_TOKEN_SECRET'))
|
||||
tweet_api = tweepy.API(auth, wait_on_rate_limit=True)
|
||||
return tweet_api
|
||||
|
|
@ -2,6 +2,7 @@ version: "3.9"
|
|||
services:
|
||||
plane-notify:
|
||||
platform: linux/amd64
|
||||
shm_size: 2gb
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import json
|
||||
import requests
|
||||
def get_avg_fuel_price():
|
||||
import pandas as pd
|
||||
from bs4 import BeautifulSoup
|
||||
try:
|
||||
response = requests.get("https://www.airnav.com/fuel/report.html")
|
||||
soup = BeautifulSoup(response.text, 'lxml') # Parse the HTML as a string
|
||||
table = soup.find_all('table')[3] # Grab the first table
|
||||
nation_wide = table.find_all('tr')[4]
|
||||
nation_wide_avg_jeta = nation_wide.find_all('td')[6]
|
||||
print("Current nationwide Jet-A fuel price avg per G is $", nation_wide_avg_jeta.text[1:])
|
||||
return(float(nation_wide_avg_jeta.text[1:]))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def fuel_calculation(aircraft_icao_type, minutes):
|
||||
"""Calculates fuel usage, price, c02 output of a flight depending on aircraft type and flight length"""
|
||||
with open("aircraft_type_fuel_consumption_rates.json", "r") as f:
|
||||
fuellist = json.loads(f.read())
|
||||
fuel_flight_info = {}
|
||||
if aircraft_icao_type in fuellist.keys():
|
||||
avg_fuel_price_per_gallon = get_avg_fuel_price()
|
||||
galph = fuellist[aircraft_icao_type]["galph"]
|
||||
fuel_used_gal = galph * (minutes/60)
|
||||
fuel_flight_info["fuel_price"] = round(fuel_used_gal * avg_fuel_price_per_gallon)
|
||||
fuel_used_kg = fuel_used_gal * 3.04
|
||||
c02_tons = (fuel_used_kg * 3.15 ) / 907.185
|
||||
fuel_flight_info['fuel_used_kg'] = round(fuel_used_kg)
|
||||
fuel_flight_info["fuel_used_gal"] = round(fuel_used_gal)
|
||||
fuel_flight_info['fuel_used_lters'] = round(fuel_used_gal*3.78541)
|
||||
fuel_flight_info["fuel_used_lbs"] = round(fuel_used_kg * 2.20462)
|
||||
fuel_flight_info["c02_tons"] = round(c02_tons) if c02_tons > 1 else round(c02_tons, 4)
|
||||
print ("Fuel info", fuel_flight_info)
|
||||
return fuel_flight_info
|
||||
else:
|
||||
print("Can't calculate fuel info unknown aircraft ICAO type")
|
||||
return None
|
||||
|
||||
def fuel_message(fuel_info):
|
||||
cost = "{:,}".format(fuel_info['fuel_price'])
|
||||
gallons = "{:,}".format(fuel_info['fuel_used_gal'])
|
||||
lters = "{:,}".format(fuel_info['fuel_used_lters'])
|
||||
lbs = "{:,}".format(fuel_info['fuel_used_lbs'])
|
||||
kgs = "{:,}".format(fuel_info['fuel_used_kg'])
|
||||
fuel_message = f"\n~ {gallons} gallons ({lters} liters). \n~ {lbs} lbs ({kgs} kg) of jet fuel used. \n~ ${cost} cost of fuel. \n~ {fuel_info['c02_tons']} tons of CO2 emissions."
|
||||
print(fuel_message)
|
||||
return fuel_message
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import requests
|
||||
import json
|
||||
def post_fb(page_id, file_path, message, access_token):
|
||||
"""Posts to Facebook with Image"""
|
||||
import os
|
||||
file_name = os.path.basename(file_path)
|
||||
files= {'image':(file_name, open(file_path, 'rb'), "multipart/form-data")}
|
||||
url = f"https://graph.facebook.com/{page_id}/photos?message={message}&access_token={access_token}"
|
||||
resp = requests.post(url, files=files)
|
||||
resp.raise_for_status()
|
||||
print("Facebook Post Response: ", resp.json())
|
||||
return resp.json()
|
||||
|
||||
def get_fb_post_image_link(post_id, access_token):
|
||||
"""Returns Highest Resolution image link of a Facebook Post by FBID"""
|
||||
url = f"https://graph.facebook.com/{post_id}?fields=images&access_token={access_token}"
|
||||
resp = requests.get(url)
|
||||
resp.raise_for_status()
|
||||
image_url = resp.json()['images'][0]['source']
|
||||
print("Highest Resoulution Image URL for FBID", post_id, "is", image_url)
|
||||
return image_url
|
||||
|
||||
def post_to_instagram(ig_user_id, access_token, image_url, caption):
|
||||
"""Posts to Instagram"""
|
||||
post_url = f'https://graph.facebook.com/v13.0/{ig_user_id}/media'
|
||||
payload = {
|
||||
'caption': caption,
|
||||
'access_token': access_token,
|
||||
'image_url': image_url
|
||||
}
|
||||
resp = requests.post(post_url, data=payload)
|
||||
resp.raise_for_status()
|
||||
print("IG Media Response:", resp.json())
|
||||
result = json.loads(resp.text)
|
||||
if 'id' in result:
|
||||
creation_id = result['id']
|
||||
second_url = f'https://graph.facebook.com/v13.0/{ig_user_id}/media_publish'
|
||||
second_payload = {
|
||||
'creation_id': creation_id,
|
||||
'access_token':access_token
|
||||
}
|
||||
resp = requests.post(second_url, data=second_payload)
|
||||
resp.raise_for_status()
|
||||
print('Posted to Instagram', caption, "IG response:", resp.json())
|
||||
else:
|
||||
print('Could not post to Instagram: ', resp.json())
|
||||
def post_to_meta_both(fb_page_id, ig_user_id, file_path, message, access_token):
|
||||
"""Posts to Facebook and Instagram"""
|
||||
post_info = post_fb(fb_page_id, file_path, message, access_token)
|
||||
fb_image_link = get_fb_post_image_link(post_info['id'], access_token)
|
||||
post_to_instagram(ig_user_id, access_token, fb_image_link, message)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
def append_airport(filename, airport):
|
||||
def append_airport(filename, airport, text_credit=None):
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
distance_mi = airport['distance_mi']
|
||||
icao = airport['icao']
|
||||
|
|
@ -27,23 +27,30 @@ def append_airport(filename, airport):
|
|||
#Header Box
|
||||
draw.rectangle(((401, 738), (549, 760)), fill= navish)
|
||||
#ADSBX Logo
|
||||
draw.rectangle(((658, 762), (800, 782)), fill= white)
|
||||
adsbx = Image.open("./dependencies/ADSBX_Logo.png")
|
||||
adsbx = adsbx.resize((25, 25), Image.ANTIALIAS)
|
||||
image.paste(adsbx, (632, 757), adsbx)
|
||||
#
|
||||
# adsbx = Image.open("./dependencies/ADSBX_Logo.png")
|
||||
# adsbx = adsbx.resize((25, 25), Image.ANTIALIAS)
|
||||
# image.paste(adsbx, (632, 757), adsbx)
|
||||
#Create Text
|
||||
#ADSBX Credit
|
||||
(x, y) = (660, 760)
|
||||
text = "adsbexchange.com"
|
||||
draw.text((x, y), text, fill=black, font=head_font)
|
||||
if text_credit is not None:
|
||||
draw.rectangle(((658, 762), (800, 782)), fill= white)
|
||||
(x, y) = (660, 760)
|
||||
text = text_credit
|
||||
draw.text((x, y), text, fill=black, font=head_font)
|
||||
#Nearest Airport Header
|
||||
(x, y) = (422, 740)
|
||||
text = "Nearest Airport"
|
||||
draw.text((x, y), text, fill=white, font=head_font)
|
||||
#ICAO | IATA
|
||||
(x, y) = (330, 765)
|
||||
text = iata + " / " + icao
|
||||
draw.text((x, y), text, fill=black, font=font)
|
||||
if airport['iata_code'] != '' and airport['icao'] != '':
|
||||
airport_codes = airport['iata_code'] + " / " + airport['icao']
|
||||
elif airport['icao'] != '':
|
||||
airport_codes = airport['icao']
|
||||
else:
|
||||
airport_codes = airport['ident']
|
||||
draw.text((x, y), airport_codes, fill=black, font=font)
|
||||
#Distance
|
||||
(x, y) = (460, 765)
|
||||
text = str(round(distance_mi, 2)) + "mi / " + str(round(distance_km, 2)) + "km away"
|
||||
|
|
|
|||
399
planeClass.py
399
planeClass.py
|
|
@ -7,8 +7,31 @@ class Plane:
|
|||
"""Initializes a plane object from its config file and given icao."""
|
||||
self.icao = icao.upper()
|
||||
self.callsign = None
|
||||
self.reg = None
|
||||
self.config = config
|
||||
self.config_path = config_path
|
||||
self.overrides = {}
|
||||
if self.config.has_option('DATA', 'OVERRIDE_REG'):
|
||||
self.reg = self.config.get('DATA', 'OVERRIDE_REG')
|
||||
self.overrides['reg'] = self.reg
|
||||
else:
|
||||
self.reg = None
|
||||
if self.config.has_option('DATA', 'OVERRIDE_ICAO_TYPE'):
|
||||
self.type = self.config.get('DATA', 'OVERRIDE_ICAO_TYPE')
|
||||
self.overrides['type'] = self.type
|
||||
else:
|
||||
self.type = None
|
||||
if self.config.has_option('DATA', 'OVERRIDE_ICAO_TYPE'):
|
||||
self.overrides['typelong'] = self.config.get('DATA', 'OVERRIDE_TYPELONG')
|
||||
if self.config.has_option('DATA', 'OVERRIDE_OWNER'):
|
||||
self.overrides['ownop'] = self.config.get('DATA', 'OVERRIDE_OWNER')
|
||||
if self.config.has_option('DATA', 'CONCEAL_AC_ID'):
|
||||
self.conceal_ac_id = self.config.getboolean('DATA', 'CONCEAL_AC_ID')
|
||||
else:
|
||||
self.conceal_ac_id = False
|
||||
if self.config.has_option('DATA', 'CONCEAL_PIA'):
|
||||
self.conceal_pia = self.config.getboolean('DATA', 'CONCEAL_PIA')
|
||||
else:
|
||||
self.conceal_pia = False
|
||||
self.conf_file_path = config_path
|
||||
self.alt_ft = None
|
||||
self.below_desired_ft = None
|
||||
|
|
@ -41,26 +64,35 @@ class Plane:
|
|||
self.track = None
|
||||
self.last_track = None
|
||||
self.circle_history = None
|
||||
self.nearest_from_airport = None
|
||||
if self.config.has_option('DATA', 'DATA_LOSS_MINS'):
|
||||
self.data_loss_mins = self.config.getint('DATA', 'DATA_LOSS_MINS')
|
||||
else:
|
||||
self.data_loss_mins = Plane.main_config.getint('DATA', 'DATA_LOSS_MINS')
|
||||
#Setup Tweepy
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
from defTweet import tweepysetup
|
||||
self.tweet_api = tweepysetup(self.config)
|
||||
#Setup PushBullet
|
||||
if self.config.getboolean('PUSHBULLET', 'ENABLE'):
|
||||
from pushbullet import Pushbullet
|
||||
self.pb = Pushbullet(self.config['PUSHBULLET']['API_KEY'])
|
||||
self.pb_channel = self.pb.get_channel(self.config.get('PUSHBULLET', 'CHANNEL_TAG'))
|
||||
if self.config.getboolean('TWITTER', 'ENABLE') and Plane.main_config.getboolean('TWITTER', 'ENABLE'):
|
||||
import tweepy
|
||||
twitter_app_auth = tweepy.OAuthHandler(Plane.main_config.get('TWITTER', 'CONSUMER_KEY'), Plane.main_config.get('TWITTER', 'CONSUMER_SECRET'))
|
||||
twitter_app_auth.set_access_token(config.get('TWITTER', 'ACCESS_TOKEN'), config.get('TWITTER', 'ACCESS_TOKEN_SECRET'))
|
||||
self.tweet_api = tweepy.API(twitter_app_auth, wait_on_rate_limit=True)
|
||||
try:
|
||||
self.latest_tweet_id = self.tweet_api.user_timeline(count = 1)[0]
|
||||
except IndexError:
|
||||
self.latest_tweet_id = None
|
||||
def run_opens(self, ac_dict):
|
||||
#Parse OpenSky Vector
|
||||
from colorama import Fore, Back, Style
|
||||
self.printheader("head")
|
||||
self.print_header("BEGIN")
|
||||
#print (Fore.YELLOW + "OpenSky Sourced Data: ", ac_dict)
|
||||
try:
|
||||
self.__dict__.update({'icao' : ac_dict.icao24.upper(), 'callsign' : ac_dict.callsign, 'latitude' : ac_dict.latitude, 'longitude' : ac_dict.longitude, 'on_ground' : bool(ac_dict.on_ground), 'squawk' : ac_dict.squawk, 'track' : float(ac_dict.heading)})
|
||||
self.__dict__.update({
|
||||
'icao' : ac_dict.icao24.upper(),
|
||||
'callsign' : ac_dict.callsign,
|
||||
'latitude' : ac_dict.latitude,
|
||||
'longitude' : ac_dict.longitude,
|
||||
'on_ground' : bool(ac_dict.on_ground),
|
||||
'squawk' : ac_dict.squawk,
|
||||
'track' : float(ac_dict.true_track)})
|
||||
if ac_dict.baro_altitude != None:
|
||||
self.alt_ft = round(float(ac_dict.baro_altitude) * 3.281)
|
||||
elif self.on_ground:
|
||||
|
|
@ -68,18 +100,19 @@ class Plane:
|
|||
from mictronics_parse import get_aircraft_reg_by_icao, get_type_code_by_icao
|
||||
self.reg = get_aircraft_reg_by_icao(self.icao)
|
||||
self.type = get_type_code_by_icao(self.icao)
|
||||
self.last_pos_datetime = datetime.fromtimestamp(ac_dict.time_position)
|
||||
except Exception as e:
|
||||
if ac_dict.time_position is not None:
|
||||
self.last_pos_datetime = datetime.fromtimestamp(ac_dict.time_position)
|
||||
except ValueError as e:
|
||||
print("Got data but some data is invalid!")
|
||||
print(e)
|
||||
self.printheader("foot")
|
||||
self.print_header("END")
|
||||
else:
|
||||
self.feeding = True
|
||||
self.run_check()
|
||||
def run_adsbx_v1(self, ac_dict):
|
||||
#Parse ADBSX V1 Vector
|
||||
from colorama import Fore, Back, Style
|
||||
self.printheader("head")
|
||||
self.print_header("BEGIN")
|
||||
#print (Fore.YELLOW +"ADSBX Sourced Data: ", ac_dict, Style.RESET_ALL)
|
||||
try:
|
||||
#postime is divided by 1000 to get seconds from milliseconds, from timestamp expects secs.
|
||||
|
|
@ -92,7 +125,7 @@ class Plane:
|
|||
print("Got data but some data is invalid!")
|
||||
print(e)
|
||||
print (Fore.YELLOW +"ADSBX Sourced Data: ", ac_dict, Style.RESET_ALL)
|
||||
self.printheader("foot")
|
||||
self.print_header("END")
|
||||
else:
|
||||
self.feeding = True
|
||||
self.run_check()
|
||||
|
|
@ -100,7 +133,7 @@ class Plane:
|
|||
def run_adsbx_v2(self, ac_dict):
|
||||
#Parse ADBSX V2 Vector
|
||||
from colorama import Fore, Back, Style
|
||||
self.printheader("head")
|
||||
self.print_header("BEGIN")
|
||||
print(ac_dict)
|
||||
try:
|
||||
self.__dict__.update({'icao' : ac_dict['hex'].upper(), 'latitude' : float(ac_dict['lat']), 'longitude' : float(ac_dict['lon']), 'speed': ac_dict['gs']})
|
||||
|
|
@ -116,6 +149,8 @@ class Plane:
|
|||
self.on_ground = True
|
||||
if ac_dict.get('flight') is not None:
|
||||
self.callsign = ac_dict.get('flight').strip()
|
||||
else:
|
||||
self.callsign = None
|
||||
if ac_dict.get('dbFlags') is not None:
|
||||
self.db_flags = ac_dict['dbFlags']
|
||||
if 'nav_modes' in ac_dict:
|
||||
|
|
@ -142,7 +177,7 @@ class Plane:
|
|||
print("Got data but some data is invalid!")
|
||||
print(e)
|
||||
print (Fore.YELLOW +"ADSBX Sourced Data: ", ac_dict, Style.RESET_ALL)
|
||||
self.printheader("foot")
|
||||
self.print_header("END")
|
||||
else:
|
||||
#Error Handling for bad data, sometimes it would seem to be ADSB Decode error
|
||||
if (not self.on_ground) and self.speed <= 10:
|
||||
|
|
@ -169,13 +204,19 @@ class Plane:
|
|||
]
|
||||
output = list(filter(None, output))
|
||||
return tabulate(output, [], 'fancy_grid')
|
||||
def printheader(self, type):
|
||||
def print_header(self, note):
|
||||
from colorama import Fore, Back, Style
|
||||
if type == "head":
|
||||
header = str("--------- " + self.conf_file_path + " ---------------------------- ICAO: " + self.icao + " ---------------------------------------")
|
||||
elif type == "foot":
|
||||
header = "----------------------------------------------------------------------------------------------------"
|
||||
print(Back.MAGENTA + header[0:100] + Style.RESET_ALL)
|
||||
if note == "BEGIN":
|
||||
header = f"---BEGIN---------{self.conf_file_path}"
|
||||
elif note == "END":
|
||||
header = f"---END"
|
||||
remaning_len = 85 - len(header)
|
||||
for x in range(0, remaning_len):
|
||||
header += "-"
|
||||
header += f"ICAO: {self.icao}---"
|
||||
if note =="END":
|
||||
header += Style.RESET_ALL + "\n"
|
||||
print(Back.MAGENTA + header + Style.RESET_ALL)
|
||||
def get_time_since(self, datetime_obj):
|
||||
if datetime_obj != None:
|
||||
time_since = datetime.now() - datetime_obj
|
||||
|
|
@ -186,15 +227,18 @@ class Plane:
|
|||
if self.config.has_option('MAP', 'OVERLAYS'):
|
||||
overlays = self.config.get('MAP', 'OVERLAYS')
|
||||
else:
|
||||
overlays = ""
|
||||
overlays = "null"
|
||||
return overlays
|
||||
def route_info(self):
|
||||
from lookup_route import lookup_route, clean_data
|
||||
def route_format(extra_route_info, type):
|
||||
from defAirport import get_airport_by_icao
|
||||
to_airport = get_airport_by_icao(self.known_to_airport)
|
||||
code = to_airport['iata_code'] if to_airport['iata_code'] != "" else to_airport['icao']
|
||||
airport_text = f"{code}, {to_airport['name']}"
|
||||
if to_airport:
|
||||
code = to_airport['iata_code'] if to_airport['iata_code'] != "" else to_airport['icao']
|
||||
airport_text = f"{code}, {to_airport['name']}"
|
||||
else:
|
||||
airport_text = f"{self.known_to_airport}"
|
||||
if 'time_to' in extra_route_info.keys() and type != "divert":
|
||||
arrival_rel = "in ~" + extra_route_info['time_to']
|
||||
else:
|
||||
|
|
@ -206,7 +250,10 @@ class Plane:
|
|||
header = "Now going to"
|
||||
elif type == "divert":
|
||||
header = "Now diverting to"
|
||||
area = f"{to_airport['municipality']}, {to_airport['region']}, {to_airport['iso_country']}"
|
||||
if to_airport:
|
||||
area = f"{to_airport['municipality']}, {to_airport['region']}, {to_airport['iso_country']}"
|
||||
else:
|
||||
area = ""
|
||||
route_to = f"{header} {area} ({airport_text})" + (f" arriving {arrival_rel}" if arrival_rel is not None else "")
|
||||
else:
|
||||
if type == "inital":
|
||||
|
|
@ -243,7 +290,7 @@ class Plane:
|
|||
|
||||
return route_to
|
||||
def run_empty(self):
|
||||
self.printheader("head")
|
||||
self.print_header("BEGIN")
|
||||
self.feeding = False
|
||||
self.run_check()
|
||||
def run_check(self):
|
||||
|
|
@ -352,17 +399,19 @@ class Plane:
|
|||
area = f"{municipality}, {state}"
|
||||
location_string = (f"{area}, {country_code}")
|
||||
print (Fore.GREEN + "Country Code:", country_code, "State:", state, "Municipality:", municipality + Style.RESET_ALL)
|
||||
title_switch = {
|
||||
"reg": self.reg,
|
||||
"callsign": self.callsign,
|
||||
"icao": self.icao,
|
||||
}
|
||||
dynamic_title = self.callsign or self.reg or self.icao
|
||||
#Set Discord Title
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
self.dis_title = (title_switch.get(self.config.get('DISCORD', 'TITLE')) or "NA").strip() if self.config.get('DISCORD', 'TITLE') in title_switch.keys() else self.config.get('DISCORD', 'TITLE')
|
||||
if self.config.get('DISCORD', 'TITLE') in ["DYNAMIC", "callsign"]:
|
||||
self.dis_title = dynamic_title
|
||||
else:
|
||||
self.dis_title = self.config.get('DISCORD', 'TITLE')
|
||||
#Set Twitter Title
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
self.twitter_title = (title_switch.get(self.config.get('TWITTER', 'TITLE')) or "NA") if self.config.get('TWITTER', 'TITLE') in title_switch.keys() else self.config.get('TWITTER', 'TITLE')
|
||||
if self.config.get('TWITTER', 'TITLE') in ["DYNAMIC", "callsign"]:
|
||||
self.twitter_title = dynamic_title
|
||||
else:
|
||||
self.twitter_title = self.config.get('TWITTER', 'TITLE')
|
||||
#Takeoff and Land Notification
|
||||
if self.tookoff or self.landed:
|
||||
route_to = None
|
||||
|
|
@ -392,19 +441,21 @@ class Plane:
|
|||
self.takeoff_time = None
|
||||
elif self.landed:
|
||||
landed_time_msg = None
|
||||
landed_time = None
|
||||
|
||||
message = (f"{type_header} {location_string}.") + ("" if route_to is None else f" {route_to}.") + ((f" {landed_time_msg}") if landed_time_msg != None else "")
|
||||
print (message)
|
||||
#Google Map or tar1090 screenshot
|
||||
if self.config.get('MAP', 'OPTION') == "GOOGLESTATICMAP":
|
||||
if Plane.main_config.get('MAP', 'OPTION') == "GOOGLESTATICMAP":
|
||||
from defMap import getMap
|
||||
getMap((municipality + ", " + state + ", " + country_code), self.map_file_name)
|
||||
elif self.config.get('MAP', 'OPTION') == "ADSBX":
|
||||
elif Plane.main_config.get('MAP', 'OPTION') == "ADSBX":
|
||||
from defSS import get_adsbx_screenshot
|
||||
|
||||
url_params = f"icao={self.icao}&zoom=9&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays=" + self.get_adsbx_map_overlays()
|
||||
get_adsbx_screenshot(self.map_file_name, url_params)
|
||||
url_params = f"largeMode=2&hideButtons&hideSidebar&mapDim=0&zoom=10&icao={self.icao}&overlays={self.get_adsbx_map_overlays()}&limitupdates=0"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, overrides=self.overrides, conceal_ac_id=self.conceal_ac_id, conceal_pia=self.conceal_pia)
|
||||
from modify_image import append_airport
|
||||
append_airport(self.map_file_name, nearest_airport_dict)
|
||||
text_credit = self.config.get('MAP', 'TEXT_CREDIT') if self.config.has_option('MAP', 'TEXT_CREDIT') else None
|
||||
append_airport(self.map_file_name, nearest_airport_dict, text_credit)
|
||||
else:
|
||||
raise ValueError("Map option not set correctly in this planes conf")
|
||||
#Telegram
|
||||
|
|
@ -414,23 +465,52 @@ class Plane:
|
|||
sendTeleg(photo, message, self.config)
|
||||
#Discord
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
dis_message = f"{self.dis_title} {message}".strip()
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') else None
|
||||
sendDis(dis_message, self.config, self.map_file_name, role_id = role_id)
|
||||
#PushBullet
|
||||
if self.config.getboolean('PUSHBULLET', 'ENABLE'):
|
||||
with open(self.map_file_name, "rb") as pic:
|
||||
map_data = self.pb.upload_file(pic, "Tookoff IMG" if self.tookoff else "Landed IMG")
|
||||
self.pb_channel.push_note(self.config.get('PUSHBULLET', 'TITLE'), message)
|
||||
self.pb_channel.push_file(**map_data)
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') and self.config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis(message, self.config, role_id, self.map_file_name)
|
||||
#Twitter
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
twitter_media_map_obj = self.tweet_api.media_upload(self.map_file_name)
|
||||
alt_text = f"Reg: {self.reg} On Ground: {str(self.on_ground)} Alt: {str(self.alt_ft)} Last Contact: {str(time_since_contact)} Trigger: {trigger_type}"
|
||||
self.tweet_api.create_media_metadata(media_id= twitter_media_map_obj.media_id, alt_text= alt_text)
|
||||
self.latest_tweet_id = self.tweet_api.update_status(status = ((self.twitter_title + " " + message).strip()), media_ids=[twitter_media_map_obj.media_id]).id
|
||||
import tweepy
|
||||
try:
|
||||
twitter_media_map_obj = self.tweet_api.media_upload(self.map_file_name)
|
||||
alt_text = f"Reg: {self.reg} On Ground: {str(self.on_ground)} Alt: {str(self.alt_ft)} Last Contact: {str(time_since_contact)} Trigger: {trigger_type}"
|
||||
self.tweet_api.create_media_metadata(media_id= twitter_media_map_obj.media_id, alt_text= alt_text)
|
||||
self.latest_tweet_id = self.tweet_api.update_status(status = ((self.twitter_title + " " + message).strip()), media_ids=[twitter_media_map_obj.media_id]).id
|
||||
except tweepy.errors.TweepyException as e:
|
||||
raise
|
||||
#Meta
|
||||
if self.config.has_option('META', 'ENABLE') and self.config.getboolean('META', 'ENABLE'):
|
||||
from meta_toolkit import post_to_meta_both
|
||||
post_to_meta_both(self.config.get("META", "FB_PAGE_ID"), self.config.get("META", "IG_USER_ID"), self.map_file_name, message, self.config.get("META", "ACCESS_TOKEN"))
|
||||
os.remove(self.map_file_name)
|
||||
if self.landed:
|
||||
if nearest_airport_dict is not None and self.nearest_from_airport is not None and nearest_airport_dict['icao'] != self.nearest_from_airport:
|
||||
from defAirport import get_airport_by_icao
|
||||
from geopy.distance import geodesic
|
||||
landed_airport = nearest_airport_dict
|
||||
nearest_from_airport = get_airport_by_icao(self.nearest_from_airport)
|
||||
from_coord = (nearest_from_airport['latitude_deg'], nearest_from_airport['longitude_deg'])
|
||||
to_coord = (landed_airport['latitude_deg'], landed_airport['longitude_deg'])
|
||||
distance_mi = float(geodesic(from_coord, to_coord).mi)
|
||||
distance_nm = distance_mi / 1.150779448
|
||||
distance_message = f"{'{:,}'.format(round(distance_mi))} mile ({'{:,}'.format(round(distance_nm))} NM) flight from {nearest_from_airport['iata_code'] if nearest_from_airport['iata_code'] != '' else nearest_from_airport['ident']} to {nearest_airport_dict['iata_code'] if nearest_airport_dict['iata_code'] != '' else nearest_airport_dict['ident']}\n"
|
||||
else:
|
||||
distance_message = ""
|
||||
if landed_time is not None and self.type is not None:
|
||||
print("Running fuel info calc")
|
||||
flight_time_min = landed_time.total_seconds() / 60
|
||||
from fuel_calc import fuel_calculation, fuel_message
|
||||
fuel_info = fuel_calculation(self.type, flight_time_min)
|
||||
if fuel_info is not None:
|
||||
fuel_message = fuel_message(fuel_info)
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
dis_message = f"{self.dis_title} {distance_message} \nFlight Fuel Info ```{fuel_message}```".strip()
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') and self.config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis(dis_message, self.config, role_id)
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
try:
|
||||
self.latest_tweet_id = self.tweet_api.update_status(status = ((self.twitter_title + " " + distance_message + " " + fuel_message).strip()), in_reply_to_status_id = self.latest_tweet_id).id
|
||||
except tweepy.errors.TweepyException as e:
|
||||
raise
|
||||
self.latest_tweet_id = None
|
||||
self.recheck_route_time = None
|
||||
self.known_to_airport = None
|
||||
|
|
@ -445,15 +525,15 @@ class Plane:
|
|||
if self.config.has_section('TELEGRAM') and self.config.getboolean('TELEGRAM', 'ENABLE'):
|
||||
message = f"{self.dis_title} {route_to}".strip()
|
||||
photo = open(self.map_file_name, "rb")
|
||||
from defTelegram import sendTeleg
|
||||
sendTeleg(photo, message, self.config)
|
||||
#Discord
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
dis_message = f"{self.dis_title} {route_to}".strip()
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') else None
|
||||
sendDis(dis_message, self.config, role_id = role_id)
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') and self.config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis(dis_message, self.config, role_id)
|
||||
#Twitter
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
#tweet = self.tweet_api.user_timeline(count = 1)[0]
|
||||
self.latest_tweet_id = self.tweet_api.update_status(status = f"{self.twitter_title} {route_to}".strip(), in_reply_to_status_id = self.latest_tweet_id).id
|
||||
|
||||
if self.circle_history is not None:
|
||||
|
|
@ -479,15 +559,15 @@ class Plane:
|
|||
squawk_message = (f"{self.dis_title} Squawking {self.last_emergency[1]} {emergency_squawks[self.squawk]}").strip()
|
||||
print(squawk_message)
|
||||
#Google Map or tar1090 screenshot
|
||||
if self.config.get('MAP', 'OPTION') == "GOOGLESTATICMAP":
|
||||
if Plane.main_config.get('MAP', 'OPTION') == "GOOGLESTATICMAP":
|
||||
getMap((municipality + ", " + state + ", " + country_code), self.map_file_name)
|
||||
if self.config.get('MAP', 'OPTION') == "ADSBX":
|
||||
if Plane.main_config.get('MAP', 'OPTION') == "ADSBX":
|
||||
from defSS import get_adsbx_screenshot
|
||||
url_params = f"icao={self.icao}&zoom=9&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays=" + self.get_adsbx_map_overlays()
|
||||
get_adsbx_screenshot(self.map_file_name, url_params)
|
||||
url_params = f"largeMode=2&hideButtons&hideSidebar&mapDim=0&zoom=10&icao={self.icao}&overlays={self.get_adsbx_map_overlays()}&limitupdates=0"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, overrides=self.overrides, conceal_ac_id=self.conceal_ac_id, conceal_pia=self.conceal_pia)
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
dis_message = (self.dis_title + " " + squawk_message)
|
||||
sendDis(dis_message, self.config, self.map_file_name)
|
||||
sendDis(dis_message, self.config, None, self.map_file_name)
|
||||
os.remove(self.map_file_name)
|
||||
#Realizes first time seeing emergency, stores time and type
|
||||
elif self.squawk in emergency_squawks.keys() and not self.emergency_already_triggered and not self.on_ground:
|
||||
|
|
@ -506,9 +586,9 @@ class Plane:
|
|||
dis_message = (self.dis_title + " " + mode + " mode enabled.")
|
||||
if mode == "Approach":
|
||||
from defSS import get_adsbx_screenshot
|
||||
url_params = f"icao={self.icao}&zoom=9&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays={self.get_adsbx_map_overlays()}"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params)
|
||||
sendDis(dis_message, self.config, self.map_file_name)
|
||||
url_params = f"largeMode=2&hideButtons&hideSidebar&mapDim=0&zoom=10&icao={self.icao}&overlays={self.get_adsbx_map_overlays()}&limitupdates=0"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, overrides=self.overrides, conceal_ac_id=self.conceal_ac_id, conceal_pia=self.conceal_pia)
|
||||
sendDis(dis_message, self.config, None, self.map_file_name)
|
||||
#elif mode in ["Althold", "VNAV", "LNAV"] and self.sel_nav_alt != None:
|
||||
# sendDis((dis_message + ", Sel Alt. " + str(self.sel_nav_alt) + ", Current Alt. " + str(self.alt_ft)), self.config)
|
||||
else:
|
||||
|
|
@ -519,7 +599,7 @@ class Plane:
|
|||
print("Nav altitude is now", self.sel_nav_alt)
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
dis_message = (self.dis_title + " Sel. alt. " + str("{:,} ft".format(self.sel_nav_alt)))
|
||||
sendDis(dis_message,self.config)
|
||||
sendDis(dis_message, self.config)
|
||||
#Circling
|
||||
if self.last_track is not None:
|
||||
import time
|
||||
|
|
@ -553,40 +633,189 @@ class Plane:
|
|||
#rp = (points.representative_point()) #A represenative point, not centroid,
|
||||
print(cent)
|
||||
#print(rp)
|
||||
distance_to_centroid = geodesic(aircraft_coords, cent.coords).mi
|
||||
distance_to_centroid = round(geodesic(aircraft_coords, cent.coords).mi, 2)
|
||||
print(f"Distance to centroid of circling coordinates {distance_to_centroid} miles")
|
||||
if distance_to_centroid <= 15:
|
||||
print("Within 15 miles of centroid, CIRCLING")
|
||||
#Finds Nearest Airport
|
||||
from defAirport import getClosestAirport
|
||||
nearest_airport_dict = getClosestAirport(self.latitude, self.longitude, ["small_airport", "medium_airport", "large_airport"])
|
||||
from calculate_headings import calculate_from_bearing, calculate_cardinal
|
||||
from_bearing = calculate_from_bearing((float(nearest_airport_dict['latitude_deg']), float(nearest_airport_dict['longitude_deg'])), (self.latitude, self.longitude))
|
||||
cardinal = calculate_cardinal(from_bearing)
|
||||
#Finds Nearest TFR or in TFR
|
||||
from shapely.geometry import MultiPoint
|
||||
from geopy.distance import geodesic
|
||||
from shapely.geometry.polygon import Polygon
|
||||
from shapely.geometry import Point
|
||||
import requests, json
|
||||
closest_tfr = None
|
||||
in_tfr = None
|
||||
if Plane.main_config.getboolean("TFRS", "ENABLE"):
|
||||
tfr_url = Plane.main_config.get("TFRS", "URL")
|
||||
response = requests.get(tfr_url, timeout=60)
|
||||
tfrs = json.loads(response.text)
|
||||
for tfr in tfrs:
|
||||
if in_tfr is not None:
|
||||
break
|
||||
elif tfr['details'] is not None and 'shapes' in tfr['details'].keys():
|
||||
for index, shape in enumerate(tfr['details']['shapes']):
|
||||
if 'txtName' not in shape.keys():
|
||||
shape['txtName'] = 'shape_'+str(index)
|
||||
polygon = None
|
||||
if shape['type'] == "poly":
|
||||
points = shape['points']
|
||||
elif shape['type'] == "circle":
|
||||
from functools import partial
|
||||
import pyproj
|
||||
from shapely.ops import transform
|
||||
from shapely.geometry import Point
|
||||
proj_wgs84 = pyproj.Proj('+proj=longlat +datum=WGS84')
|
||||
def geodesic_point_buffer(lat, lon, km):
|
||||
# Azimuthal equidistant projection
|
||||
aeqd_proj = '+proj=aeqd +lat_0={lat} +lon_0={lon} +x_0=0 +y_0=0'
|
||||
project = partial(
|
||||
pyproj.transform,
|
||||
pyproj.Proj(aeqd_proj.format(lat=lat, lon=lon)),
|
||||
proj_wgs84)
|
||||
buf = Point(0, 0).buffer(km * 1000) # distance in metres
|
||||
return transform(project, buf).exterior.coords[:]
|
||||
radius_km = float(shape['radius']) * 1.852
|
||||
b = geodesic_point_buffer(shape['lat'], shape['lon'], radius_km)
|
||||
points = []
|
||||
for coordinate in b:
|
||||
points.append([coordinate[1], coordinate[0]])
|
||||
elif shape['type'] in ["polyarc", "polyexclude"]:
|
||||
points = shape['all_points']
|
||||
aircraft_location = Point(self.latitude, self.longitude)
|
||||
if polygon is None:
|
||||
polygon = Polygon(points)
|
||||
if polygon.contains(aircraft_location):
|
||||
in_tfr = {'info': tfr, 'closest_shape_name' : shape['txtName']}
|
||||
break
|
||||
else:
|
||||
point_dists = []
|
||||
for point in points:
|
||||
from geopy.distance import geodesic
|
||||
point = tuple(point)
|
||||
point_dists.append(float((geodesic((self.latitude, self.longitude), point).mi)))
|
||||
distance = min(point_dists)
|
||||
if closest_tfr is None:
|
||||
closest_tfr = {'info': tfr, 'closest_shape_name' : shape['txtName'], 'distance' : round(distance)}
|
||||
elif distance < closest_tfr['distance']:
|
||||
closest_tfr = {'info': tfr, 'closest_shape_name' : shape['txtName'], 'distance' : round(distance)}
|
||||
if in_tfr is not None:
|
||||
for shape in in_tfr['info']['details']['shapes']:
|
||||
if shape['txtName'] == in_tfr['closest_shape_name']:
|
||||
valDistVerUpper, valDistVerLower = int(shape['valDistVerUpper']), int(shape['valDistVerLower'])
|
||||
print("In TFR based off location checking alt next", in_tfr)
|
||||
break
|
||||
if not (self.alt_ft >= valDistVerLower and self.alt_ft <= valDistVerUpper):
|
||||
if self.alt_ft > valDistVerUpper:
|
||||
in_tfr['context'] = "above"
|
||||
elif self.alt_ft < valDistVerLower:
|
||||
in_tfr['context'] = "below"
|
||||
print("But not in alt of TFR", in_tfr['context'])
|
||||
|
||||
if in_tfr is None:
|
||||
print("Closest TFR", closest_tfr)
|
||||
#Generate Map
|
||||
import staticmaps
|
||||
context = staticmaps.Context()
|
||||
context.set_tile_provider(staticmaps.tile_provider_OSM)
|
||||
if in_tfr is not None:
|
||||
shapes = in_tfr['info']['details']['shapes']
|
||||
else:
|
||||
shapes = closest_tfr['info']['details']['shapes']
|
||||
def draw_poly(context, pairs):
|
||||
pairs.append(pairs[0])
|
||||
context.add_object(
|
||||
staticmaps.Area(
|
||||
[staticmaps.create_latlng(lat, lng) for lat, lng in pairs],
|
||||
fill_color=staticmaps.parse_color("#FF000033"),
|
||||
width=2,
|
||||
color=staticmaps.parse_color("#8B0000"),
|
||||
)
|
||||
)
|
||||
return context
|
||||
for shape in shapes:
|
||||
if shape['type'] == "poly":
|
||||
pairs = shape['points']
|
||||
context = draw_poly(context, pairs)
|
||||
elif shape['type'] == "polyarc" or shape['type'] == "polyexclude":
|
||||
pairs = shape['all_points']
|
||||
context = draw_poly(context, pairs)
|
||||
elif shape['type'] =="circle":
|
||||
center = [shape['lat'], shape['lon']]
|
||||
center1 = staticmaps.create_latlng(center[0], center[1])
|
||||
context.add_object(staticmaps.Circle(center1, (float(shape['radius']) * 1.852), fill_color=staticmaps.parse_color("#FF000033"), color=staticmaps.parse_color("#8B0000"), width=2))
|
||||
context.add_object(staticmaps.Marker(center1, color=staticmaps.RED))
|
||||
def tfr_image(context, aircraft_coords):
|
||||
from PIL import Image
|
||||
heading = self.track
|
||||
heading *= -1
|
||||
im = Image.open('./dependencies/ac.png')
|
||||
im_rotate = im.rotate(heading, resample=Image.BICUBIC)
|
||||
import tempfile
|
||||
rotated_file = f"{tempfile.gettempdir()}/rotated_ac.png"
|
||||
im_rotate.save(rotated_file)
|
||||
pos = staticmaps.create_latlng(aircraft_coords[0], aircraft_coords[1])
|
||||
marker = staticmaps.ImageMarker(pos, rotated_file, origin_x=35, origin_y=35)
|
||||
context.add_object(marker)
|
||||
image = context.render_cairo(1000, 1000)
|
||||
os.remove(rotated_file)
|
||||
tfr_map_filename = f"{tempfile.gettempdir()}/{self.icao}_TFR_.png"
|
||||
image.write_to_png(tfr_map_filename)
|
||||
return tfr_map_filename
|
||||
|
||||
from defSS import get_adsbx_screenshot
|
||||
url_params = f"icao={self.icao}&zoom=10&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays={self.get_adsbx_map_overlays()}"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params)
|
||||
url_params = f"largeMode=2&hideButtons&hideSidebar&mapDim=0&zoom=10&icao={self.icao}&overlays={self.get_adsbx_map_overlays()}&limitupdates=0"
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, overrides=self.overrides, conceal_ac_id=self.conceal_ac_id, conceal_pia=self.conceal_pia)
|
||||
if nearest_airport_dict['distance_mi'] < 3:
|
||||
if "touchngo" in self.circle_history.keys():
|
||||
message = f"Doing touch and goes at {nearest_airport_dict['icao']}"
|
||||
else:
|
||||
message = f"Circling over {nearest_airport_dict['icao']} at {self.alt_ft}ft"
|
||||
message = f"Circling over {nearest_airport_dict['icao']} at {self.alt_ft}ft."
|
||||
else:
|
||||
message = f"Circling {round(nearest_airport_dict['distance_mi'], 2)}mi {cardinal} of {nearest_airport_dict['icao']}, {nearest_airport_dict['name']} at {self.alt_ft}ft"
|
||||
message = f"Circling {round(nearest_airport_dict['distance_mi'], 2)}mi {cardinal} of {nearest_airport_dict['icao']}, {nearest_airport_dict['name']} at {self.alt_ft}ft. "
|
||||
tfr_map_filename = None
|
||||
if in_tfr is not None:
|
||||
wording_context = "Inside" if 'context' not in in_tfr.keys() else "Above" if in_tfr['context'] == 'above' else "Below"
|
||||
message += f" {wording_context} TFR {in_tfr['info']['NOTAM']}, a TFR for {in_tfr['info']['Type'].title()}"
|
||||
tfr_map_filename = tfr_image(context, (self.latitude, self.longitude))
|
||||
elif in_tfr is None and closest_tfr is not None and "distance" in closest_tfr.keys() and closest_tfr["distance"] <= 20:
|
||||
message += f" {closest_tfr['distance']} miles from TFR {closest_tfr['info']['NOTAM']}, a TFR for {closest_tfr['info']['Type']}"
|
||||
tfr_map_filename = tfr_image(context, (self.latitude, self.longitude))
|
||||
elif in_tfr is None and closest_tfr is not None and "distance" not in closest_tfr.keys():
|
||||
message += f" near TFR {closest_tfr['info']['NOTAM']}, a TFR for {closest_tfr['info']['Type']}"
|
||||
raise Exception(message)
|
||||
|
||||
print(message)
|
||||
#Telegram
|
||||
if self.config.has_section('TELEGRAM') and self.config.getboolean('TELEGRAM', 'ENABLE'):
|
||||
photo = open(self.map_file_name, "rb")
|
||||
from defTelegram import sendTeleg
|
||||
sendTeleg(photo, message, self.config)
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') else None
|
||||
sendDis(message, self.config, self.map_file_name, role_id)
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') and self.config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
if tfr_map_filename is not None:
|
||||
sendDis(message, self.config, role_id, self.map_file_name, tfr_map_filename)
|
||||
elif tfr_map_filename is None:
|
||||
sendDis(message, self.config, role_id, self.map_file_name)
|
||||
if self.config.getboolean('TWITTER', 'ENABLE'):
|
||||
twitter_media_map_obj = self.tweet_api.media_upload(self.map_file_name)
|
||||
alt_text = f"Distance to centroid: {distance_to_centroid}, Total change: {total_change}"
|
||||
self.tweet_api.create_media_metadata(media_id= twitter_media_map_obj.media_id, alt_text= alt_text)
|
||||
tweet = self.tweet_api.user_timeline(count = 1)[0]
|
||||
self.latest_tweet_id = self.tweet_api.update_status(status = f"{self.twitter_title} {message}".strip(), in_reply_to_status_id = self.latest_tweet_id, media_ids=[twitter_media_map_obj.media_id]).id
|
||||
|
||||
media_ids = [twitter_media_map_obj.media_id]
|
||||
if tfr_map_filename is not None:
|
||||
twitter_media_tfr_map_obj = self.tweet_api.media_upload(tfr_map_filename)
|
||||
media_ids.append(twitter_media_tfr_map_obj.media_id)
|
||||
elif tfr_map_filename is None:
|
||||
print("No TFR Map")
|
||||
tweet = f"{self.twitter_title} {message}".strip()
|
||||
self.tweet_api.update_status(status = tweet, media_ids=media_ids)
|
||||
#Meta
|
||||
if self.config.has_option('META', 'ENABLE') and self.config.getboolean('META', 'ENABLE'):
|
||||
from meta_toolkit import post_to_meta_both
|
||||
post_to_meta_both(self.config.get("META", "FB_PAGE_ID"), self.config.get("META", "IG_USER_ID"), self.map_file_name, message, self.config.get("META", "ACCESS_TOKEN"))
|
||||
self.circle_history['triggered'] = True
|
||||
elif abs(total_change) <= 360 and self.circle_history["triggered"]:
|
||||
print("No Longer Circling, trigger cleared")
|
||||
|
|
@ -614,7 +843,7 @@ class Plane:
|
|||
hours, remainder = divmod(elapsed_time.total_seconds(), 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
print((f"Time Since Take off {int(hours)} Hours : {int(minutes)} Mins : {int(seconds)} Secs"))
|
||||
self.printheader("foot")
|
||||
self.print_header("END")
|
||||
def check_new_ras(self, ras):
|
||||
for ra in ras:
|
||||
if self.recent_ra_types == {} or ra['acas_ra']['advisory'] not in self.recent_ra_types.keys():
|
||||
|
|
@ -625,7 +854,7 @@ class Plane:
|
|||
if bool(int(ra['acas_ra']['MTE'])):
|
||||
ra_message += ", Multi threat"
|
||||
from defSS import get_adsbx_screenshot, generate_adsbx_screenshot_time_params
|
||||
url_params = f"&lat={ra['lat']}&lon={ra['lon']}&zoom=11&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays={self.get_adsbx_map_overlays()}"
|
||||
url_params = f"&lat={ra['lat']}&lon={ra['lon']}&zoom=11&largeMode=2&hideButtons&hideSidebar&mapDim=0&overlays={self.get_adsbx_map_overlays()}&limitupdates=0"
|
||||
if "threat_id_hex" in ra['acas_ra'].keys():
|
||||
from mictronics_parse import get_aircraft_reg_by_icao
|
||||
threat_reg = get_aircraft_reg_by_icao(ra['acas_ra']['threat_id_hex'])
|
||||
|
|
@ -635,13 +864,13 @@ class Plane:
|
|||
else:
|
||||
url_params += f"&icao={self.icao.lower()}&noIsolation"
|
||||
print(url_params)
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, True, True)
|
||||
get_adsbx_screenshot(self.map_file_name, url_params, True, True, overrides=self.overrides, conceal_ac_id=self.conceal_ac_id, conceal_pia=self.conceal_pia)
|
||||
|
||||
if self.config.getboolean('DISCORD', 'ENABLE'):
|
||||
from defDiscord import sendDis
|
||||
dis_message = f"{self.dis_title} {ra_message}"
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') else None
|
||||
sendDis(dis_message, self.config, self.map_file_name, role_id = role_id)
|
||||
role_id = self.config.get('DISCORD', 'ROLE_ID') if self.config.has_option('DISCORD', 'ROLE_ID') and self.config.get('DISCORD', 'ROLE_ID').strip() != "" else None
|
||||
sendDis(dis_message, self.config, role_id, self.map_file_name)
|
||||
#if twitter
|
||||
def expire_ra_types(self):
|
||||
if self.recent_ra_types != {}:
|
||||
|
|
@ -651,4 +880,4 @@ class Plane:
|
|||
print(time_since_ra)
|
||||
if time_since_ra.seconds >= 600:
|
||||
print(ra_type)
|
||||
self.recent_ra_types.pop(ra_type)
|
||||
self.recent_ra_types.pop(ra_type)
|
||||
Loading…
Reference in New Issue