Advertisement
Guest User

Canada Citizenship Application Tracker Tracker

a guest
Dec 15th, 2023
483
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.11 KB | Source Code | 0 0
  1. import yaml, time, os, dbus, logging, nio, asyncio, traceback
  2. from selenium import webdriver
  3. from selenium.webdriver.common.by import By
  4. from selenium.webdriver.firefox.options import Options as FirefoxOptions
  5. from bs4 import BeautifulSoup
  6. from datetime import datetime
  7. import subprocess
  8.  
  9. def desktop_notify(date: datetime, summary: str) -> None:
  10.     dir = os.path.dirname(os.path.realpath(__file__))
  11.     icon = os.path.join(dir, 'Canada.png')
  12.     if not os.path.exists(icon):
  13.         icon = 'dialog-information'
  14.     date_format = date.strftime('%d %B')
  15.     if date_format.startswith('0'):
  16.         date_format = date_format[1:]
  17.     session = dbus.SessionBus()
  18.     obj = session.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
  19.     interface = dbus.Interface(obj, 'org.freedesktop.Notifications')
  20.     interface.Notify('Citizenship tracker', 0, icon, date_format + ' update', summary, [], {'urgency': 2}, 0)
  21.     session.close()
  22.  
  23. def send_matrix_message(message: str, config: dict) -> None:
  24.     async def main():
  25.         client = nio.AsyncClient(f'https://' + config['matrix-home-server'], f"@{config['matrix-username']}:{config['matrix-home-server']}")
  26.         await client.login(config['matrix-password'])
  27.         await client.join(config['matrix-room'])
  28.         await client.room_send(
  29.             room_id=config['matrix-room'],
  30.             message_type='m.room.message',
  31.             content={'msgtype': 'm.text', 'body': message},
  32.         )
  33.         await client.close()
  34.     asyncio.run(main())
  35.  
  36. if __name__ == '__main__':
  37.     url = 'https://tracker-suivi.apps.cic.gc.ca/en/login'
  38.     dir = os.path.dirname(os.path.realpath(__file__))
  39.     with open(os.path.join(dir, 'config.yaml'), 'r') as f:
  40.         config = yaml.safe_load(f)
  41.     logging.basicConfig(filename=os.path.join(dir, 'tracker.log'), level=logging.INFO, format='%(asctime)s %(message)s')
  42.     logging.info(f'Starting tracker scraper')
  43.  
  44.     if os.path.exists(os.path.join(dir, 'STOP')):
  45.         logging.info(f'Exiting because of STOP file')
  46.         raise SystemExit
  47.    
  48.     try:
  49.         options = FirefoxOptions()
  50.         options.add_argument('-headless')
  51.         with webdriver.Firefox(options=options) as driver:
  52.             driver.get(url)
  53.             time.sleep(config['wait'])
  54.             element = driver.find_element(By.CSS_SELECTOR, '#uci')
  55.             element.send_keys(config['uci'])
  56.             element = driver.find_element(By.CSS_SELECTOR, '#password')
  57.             element.send_keys(config['password'])
  58.             element = driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]')
  59.             element.click()
  60.             time.sleep(config['wait'])
  61.             html = driver.page_source
  62.  
  63.         soup = BeautifulSoup(html, 'html.parser')
  64.         history = soup.select_one('#history-timeline')
  65.         items: dict[datetime, str] = {}
  66.         for item in history.children:
  67.             if item.name == 'li' and 'activity' in item['class']:
  68.                 date = item.select_one('div.date').text
  69.                 date = datetime.strptime(date, '%b %d, %Y')
  70.                 summary = item.select_one('div.info div.activity-title').text.strip()
  71.                 items[date] = summary
  72.         ceremony_status = ''
  73.         try:
  74.             h3 = soup.select_one('h3[aria-label="Citizenship ceremony"]')
  75.             summary = h3.parent
  76.             p = summary.select_one('p.chip-text')
  77.             ceremony_status = p.text.strip()
  78.         except:
  79.             pass
  80.     except Exception as e:
  81.         tb = traceback.extract_tb(e.__traceback__)
  82.         logging.info(f'Failed scraping due to {type(e).__name__} at line {tb[-1].lineno}')
  83.         raise SystemExit
  84.  
  85.     updated = False
  86.     for date, summary in items.items():
  87.         if date.date() > config['last-updated']:
  88.             logging.info(f'New update: {date.date()} {summary}')
  89.             updated = True
  90.             if config.get('notification-desktop'):
  91.                 desktop_notify(date, summary)
  92.             if config.get('notification-matrix'):
  93.                 send_matrix_message(f'Citizenship tracker update: {date.date()} {summary}', config)
  94.  
  95.     if config.get('ceremony-started-check') and ceremony_status and ceremony_status != 'Not started':
  96.         summary = f'Ceremony status changed: "{ceremony_status}"'
  97.         logging.info(summary)
  98.         updated = True
  99.         if config.get('notification-desktop'):
  100.             desktop_notify(datetime.now().date(), summary)
  101.         if config.get('notification-matrix'):
  102.             send_matrix_message(summary, config)
  103.  
  104.     if updated:
  105.         with open(os.path.join(dir, 'STOP'), 'w') as f:
  106.             pass
  107.         if config.get('notification-sound'):
  108.             try:
  109.                 subprocess.run([config['sound-player'], config['sound']])
  110.             except:
  111.                 logging.info(f'Failed to play sound')
  112.     else:
  113.         item_count = len(items)
  114.         if item_count:
  115.             logging.info(f'No updates: {item_count} items found, the most recent update is from {max(items.keys()).date()}')
  116.         else:
  117.             logging.info(f'No updates: no items found')
  118.     logging.info(f'Finished')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement