Beispiel #1
0
def poi_for_location(
        lat,
        lon) -> tuple[t.Optional[str], t.Optional[bytes]]:  # no test coverage
    try:
        gmaps = googlemaps.Client(key=config.google_api_key)
        places = gmaps.places_nearby(location=(lat, lon),
                                     radius=poi_radius)["results"]
    except Exception:
        log.error("Error getting location data", exc_info=True)
        return None, None
    place = next(
        (p for p in places if not poi_types.isdisjoint(p.get("types", []))),
        None)
    if not place:
        log.info("No interesting point of interest")
        return None, None
    name = place["name"]
    try:
        photo_data = next(p for p in place["photos"] if p["width"] >= 1000)
        ref = photo_data["photo_reference"]
        photo_itr = gmaps.places_photo(ref, max_width=2000)
        photo = b"".join([chunk for chunk in photo_itr if chunk])
    except StopIteration:
        log.info("No poi photo big enough")
        return name, None
    except Exception:
        log.error("Error getting poi photo", exc_info=True)
        return name, None
    return name, photo
Beispiel #2
0
def send_sync_reminders(conn: connection, slack_client, steps_data) -> None:
    reminder_users = queries.get_sync_reminder_users(conn)
    reminder_users_by_id = {user["id"]: user for user in reminder_users}
    for datum in steps_data:
        try:
            user_data = reminder_users_by_id[datum["gargling_id"]]
        except KeyError:
            continue
        msg = (
            f"Du gikk {datum['amount']} skritt i går, by my preliminary calculations. "
            "Husk å synce hvis dette tallet er for lavt. "
            f"Denne reminderen kan skrus av <{config.server_name}/health|her>. Stay "
            "beautiful, doll-face!"
        )
        try:
            resp = slack_client.chat_postMessage(
                text=msg, channel=user_data["slack_id"]
            )
            if isinstance(resp, Future):  # no test coverage
                # satisfy mypy
                raise Exception()
            if resp.data.get("ok") is True:
                queries.update_reminder_ts(
                    conn, ts=resp.data["ts"], id=datum["gargling_id"]
                )
                conn.commit()
        except Exception:  # no test coverage
            log.error(
                f"Error sending sync reminder for user id: {user_data['slack_id']}",
                exc_info=True,
            )
Beispiel #3
0
def render_map(map_: StaticMap,
               retry=True) -> t.Optional[Image.Image]:  # no test coverage
    try:
        img = map_.render()
    except Exception:
        if retry:
            return render_map(map_, retry=False)
        log.error("Error rendering map", exc_info=True)
        img = None
    return img
Beispiel #4
0
def address_for_location(
        lat,
        lon) -> tuple[t.Optional[str], t.Optional[str]]:  # no test coverage
    geolocator = Nominatim(user_agent=config.bot_name)
    try:
        location = geolocator.reverse(f"{lat}, {lon}", language="en")
        address = location.address
        country = location.raw.get("address", {}).get("country")
        return address, country
    except Exception:
        log.error("Error getting address for location", exc_info=True)
        return None, None
Beispiel #5
0
def get_body_data(users: list[HealthUser], date: pendulum.Date) -> list[dict]:
    all_data = []
    for user in users:  # no test coverage
        try:
            body_data = user.body(date)
        except Exception:
            log.error(
                f"Error getting {user.service.name} body data for {user.first_name}",
                exc_info=True,
            )
        else:
            if body_data is None:
                continue
            body_data["first_name"] = user.first_name
            all_data.append(body_data)
    return all_data
Beispiel #6
0
def delete_sync_reminders(conn: connection, slack_client) -> None:
    reminder_users = queries.get_sync_reminder_users(conn)
    log.info(reminder_users)
    for user in reminder_users:
        if user["last_sync_reminder_ts"] is None:  # no test coverage
            continue
        try:
            slack_client.chat_delete(
                channel=user["slack_id"], ts=user["last_sync_reminder_ts"]
            )
        except Exception:  # no test coverage
            log.error(
                f"Error deleting sync reminder for user id: {user['id']}",
                exc_info=True,
            )
        queries.update_reminder_ts(conn, ts=None, id=user["id"])
        conn.commit()
Beispiel #7
0
def weight_blocks(clients) -> t.List[dict]:
    now = pendulum.now()
    blocks = []
    for name, (client, service) in clients.items():
        try:
            weight_data = service.value.weight(client)
        except Exception:
            log.error(f"Error getting {service} weight data for {name}", exc_info=True)
            continue
        if weight_data is None:
            continue
        elapsed = (now - weight_data["datetime"]).days
        if elapsed < 2:
            desc = f"{name} veier nå *{weight_data['weight']}* kg!"
        else:
            desc = f"{name} har ikke veid seg på *{elapsed}* dager. Skjerpings!"
        blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": desc}})
    return blocks
Beispiel #8
0
def main() -> None:
    log.info("GargBot 3000 greeter starter")
    try:
        while True:
            event = Event.next()
            log.info(f"Next greeting check at: {event.until.end}, "
                     f"sleeping for {event.until.in_words()}")
            time.sleep(event.until.total_seconds())
            try:
                slack_client = SlackClient(config.slack_bot_user_token)
                conn = database.connect()
                event.func(conn, slack_client)
            except Exception:
                log.error("Error in command execution", exc_info=True)
            finally:
                conn.close()
    except KeyboardInterrupt:
        sys.exit()
Beispiel #9
0
def street_view_for_location(lat,
                             lon) -> t.Optional[bytes]:  # no test coverage
    def encode_url(domain, endpoint, params):
        params = params.copy()
        url_to_sign = endpoint + urllib.parse.urlencode(params)
        secret = config.google_api_secret
        decoded_key = base64.urlsafe_b64decode(secret)
        signature = hmac.new(decoded_key, url_to_sign.encode(), hashlib.sha1)
        encoded_signature = base64.urlsafe_b64encode(signature.digest())
        params["signature"] = encoded_signature.decode()
        encoded_url = domain + endpoint + urllib.parse.urlencode(params)
        return encoded_url

    domain = "https://maps.googleapis.com"
    metadata_endpoint = "/maps/api/streetview/metadata?"
    img_endpoint = "/maps/api/streetview?"
    params = {
        "size": "600x400",
        "location": f"{lat}, {lon}",
        "fov": 120,
        "heading": 251.74,
        "pitch": 0,
        "key": config.google_api_key,
    }
    metadata_url = encode_url(domain, metadata_endpoint, params)
    try:
        response = requests.get(metadata_url)
        metadata = response.json()
        if metadata["status"] != "OK":
            log.info(f"Metadata indicates no streetview image: {metadata}")
            return None
    except Exception:
        log.error("Error downloading streetview image metadata", exc_info=True)
        return None

    photo_url = encode_url(domain, img_endpoint, params)
    try:
        response = requests.get(photo_url)
        data = response.content
    except Exception:
        log.error("Error downloading streetview image", exc_info=True)
        return None
    return data
Beispiel #10
0
def main(options: t.Optional[dict], debug: bool = False):
    try:
        app.pool.setup()
        with app.pool.get_connection() as conn:
            pictures.queries.define_args(conn)
            conn.commit()
        app.dbx = pictures.connect_dbx()
        if debug is False:
            gunicorn_app = StandaloneApplication(app, options)
            gunicorn_app.run()
        else:
            # Workaround for a werzeug reloader bug
            # (https://github.com/pallets/flask/issues/1246)
            os.environ["PYTHONPATH"] = os.getcwd()
            app.run(debug=True)
    except Exception:
        log.error("Error in server setup", exc_info=True)
    finally:
        if app.pool.is_setup:
            app.pool.closeall()
Beispiel #11
0
def main():
    slack_client, drop_pics, quotes_db, db_connection = setup()

    log.info("GargBot 3000 task operational!")

    try:
        while True:
            time.sleep(1)
            text, channel, user = wait_for_slack_output(slack_client)

            try:
                command_str, *args = text.split()
            except ValueError:
                command_str = ""
                args = []
            command_str = command_str.lower()
            command_func = partial(
                commands.execute,
                command_str=command_str,
                args=args,
                db_connection=db_connection,
                drop_pics=drop_pics,
                quotes_db=quotes_db,
            )
            try:
                response = command_func()
            except psycopg2.OperationalError as op_exc:
                db_connection = database_manager.connect_to_database()
                try:
                    return command_func()
                except Exception as exc:
                    # OperationalError not caused by connection issue.
                    log.error("Error in command execution", exc_info=True)
                    return commands.cmd_panic(exc)

            send_response(slack_client, response, channel)

    except KeyboardInterrupt:
        sys.exit()
    finally:
        database_manager.close_database_connection(db_connection)
Beispiel #12
0
def execute(
    command_str: str,
    args: List,
    db_connection: connection,
    drop_pics: droppics.DropPics,
    quotes_db: quotes.Quotes,
) -> Dict:
    log.info(f"command: {command_str}")
    log.info(f"args: {args}")

    switch: Dict[str, Callable] = {
        "ping": cmd_ping,
        "new_channel": cmd_welcome,
        "gargbot": cmd_server_explanation,
        "hvem": partial(cmd_hvem, args, db=db_connection),
        "pic": partial(cmd_pic, args, db=db_connection, drop_pics=drop_pics),
        "quote": partial(cmd_quote,
                         args,
                         db=db_connection,
                         quotes_db=quotes_db),
        "msn": partial(cmd_msn, args, db=db_connection, quotes_db=quotes_db),
    }
    try:
        command_func = switch[command_str]
    except KeyError:
        command_func = partial(cmd_not_found, command_str)

    try:
        return command_func()
    except psycopg2.OperationalError:
        raise
    except SSLError as ssl_exc:
        # Dropbox sometimes gives SSLerrors, try again:
        try:
            return command_func()
        except Exception as exc:
            log.error("Error in command execution", exc_info=True)
            return cmd_panic(exc)
    except Exception as exc:
        log.error("Error in command execution", exc_info=True)
        return cmd_panic(exc)
Beispiel #13
0
def steps(conn: connection, users: list[HealthUser], date: pendulum.Date) -> list[dict]:
    step_amounts = []
    for user in users:
        try:
            amount = (
                user.steps(date)
                if not isinstance(user, PolarUser)
                else user.steps(date, conn)
            )
        except Exception:
            log.error(
                f"Error getting {user.service.name} steps data for {user.first_name}",
                exc_info=True,
            )
            continue
        if amount is None:  # no test coverage
            continue
        step_amounts.append(
            {"amount": amount, "gargling_id": user.gargling_id},
        )  # no test coverage
    return step_amounts
Beispiel #14
0
def execute(command_str: str, args: t.List, conn: connection,
            dbx: dropbox.Dropbox) -> t.Dict:
    log.info(f"command: {command_str}")
    log.info(f"args: {args}")

    switch: t.Dict[str, t.Callable] = {
        "ping": cmd_ping,
        "new_channel": cmd_welcome,
        "gargbot": cmd_server_explanation,
        "hvem": partial(cmd_hvem, args, conn=conn),
        "pic": partial(cmd_pic, args, conn=conn, dbx=dbx),
        "forum": partial(cmd_forum, args, conn=conn),
        "msn": partial(cmd_msn, args, conn=conn),
    }
    try:
        command_func = switch[command_str]
    except KeyError:
        command_func = partial(cmd_not_found, command_str)

    try:
        return command_func()
    except psycopg2.OperationalError:
        raise
    except (SSLError, dropbox.exceptions.ApiError):
        # Dropbox sometimes gives SSLerrors, (or ApiError if file not there) try again:
        try:
            log.error("SSLerror/ApiError, retrying", exc_info=True)
            return command_func()
        except Exception as exc:
            log.error("Error in command execution", exc_info=True)
            return cmd_panic(exc)
    except Exception as exc:
        log.error("Error in command execution", exc_info=True)
        return cmd_panic(exc)
Beispiel #15
0
def main():
    slack_client, dbx, conn = setup()

    log.info("GargBot 3000 task operational!")
    try:
        while True:
            time.sleep(1)
            text, channel, user, thread_ts = wait_for_slack_output(
                slack_client)

            try:
                command_str, *args = text.split()
            except ValueError:
                command_str = ""
                args = []
            command_str = command_str.lower()
            command_func = partial(commands.execute,
                                   command_str=command_str,
                                   args=args,
                                   conn=conn,
                                   dbx=dbx)
            try:
                response = command_func()
            except psycopg2.OperationalError:
                conn = database.connect()
                try:
                    response = command_func()
                except Exception as exc:
                    # OperationalError not caused by connection issue.
                    log.error("Error in command execution", exc_info=True)
                    response = commands.cmd_panic(exc)

            send_response(slack_client, response, channel, thread_ts)

    except KeyboardInterrupt:
        sys.exit()
    finally:
        conn.close()
Beispiel #16
0
def steps_blocks(clients) -> t.List[dict]:
    step_amounts = []
    for name, (client, service) in clients.items():
        try:
            steps = service.value.steps(client)
        except Exception:
            log.error(f"Error getting {service} steps data for {name}", exc_info=True)
            continue
        if steps is None:
            continue
        step_amounts.append((steps, name))
    if not step_amounts:
        return []
    step_amounts.sort(reverse=True)
    steps, name = step_amounts[0]
    desc = f"{name} gikk *{steps}* skritt i går. "
    if len(step_amounts) > 1:
        desc = desc.replace("gikk", "(:star:) gikk")
        desc += ", ".join(
            [f"{name} gikk *{steps}* skritt" for steps, name in step_amounts[1:]]
        )
        desc += "."
    return [{"type": "section", "text": {"type": "mrkdwn", "text": desc}}]