iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔖

[Automation Project #1] Automating Daily Body Temperature Log Submission

に公開

This is my first article, so the content might be a bit rough, but please bear with me.

As part one of my automation project, I've decided to automate the task of submitting my daily body temperature via Google Forms for school. It’s incredibly tedious, so I’m going to automate it. (Don't worry, I am actually measuring my temperature!)

Environment

Language: Python 3.8 series
OS: Windows 10 (Development) / Ubuntu 20.04LTS (Execution)
Editor: PyCharm

File Structure

/Auto-Google-Forms
├ bot.py / Main code
└ cfg.json / Configuration file

Main Topic

Entire Code
main.py
# -*- coding: utf-8 -*-

import json
import os.path
import random
from datetime import datetime as dt

import requests
from discord import Client, Embed, Colour
from discord.ext import tasks
from dotenv import load_dotenv

client = Client()
ch = CHANNEL_ID # Specify the channel ID where you want to send logs
load_dotenv()


def setting_time_set():
    setting_time_h = random.randint(6, 8)
    if setting_time_h == 8:
        setting_time_m = random.randint(0, 30)
    else:
        setting_time_m = random.randint(0, 59)

    return str(f"{setting_time_h:02}:{setting_time_m:02}")


def body_temperature_set():
    return random.choice(["36.4", "36.5", "36.6"])


async def send(bts):
    setting_file_path = "/home/seiwell/DiscordBot/Auto-Google-Forms/cfg.json"
    with open(setting_file_path, "r", encoding="utf-8")as f:
        cfg = json.load(f)
        cfg["output"]["ans_1"] = f"{dt.now().strftime('%Y-%m-%d')}"

        cfg["output"]["ans_3"] = f"{bts}"

        params = {"entry.{}".format(cfg["entry"][k]): cfg["output"][k] for k in cfg["entry"].keys()}
        res = requests.get(cfg["form_url"] + "formResponse", params=params)

        if res.status_code == 200:
            em = Embed(title="Log Info", description=f"[URL]({res.url})", color=Colour.green())
            em.add_field(name="Temperature Info", value=bts)
            em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
            await client.get_channel(ch).send("<@343956207754805251>")
            await client.get_channel(ch).send(embed=em)

        else:
            res.raise_for_status()
            em = Embed(title="Log Info", description="Could not send successfully.", color=Colour.red())
            em.add_field(name="Temperature Info", value=bts)
            em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
            await client.get_channel(ch).send("<@343956207754805251>")
            await client.get_channel(ch).send(embed=em)

sts = setting_time_set()
bts = body_temperature_set()


@client.event
async def on_ready():
    em = Embed(title="Startup Log", color=Colour.orange())
    em.add_field(name="Next scheduled send time", value=f"{sts}")
    em.add_field(name="Temperature to be sent", value=f"{bts}")
    em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
    await client.get_channel(ch).send("<@343956207754805251>")
    await client.get_channel(ch).send(embed=em)


@client.event
async def on_message(message):
    if message.content == "/now_send":
        await send(bts=body_temperature_set())
        await message.channel.send(f"{message.author.mention}-> Send complete")


@tasks.loop(seconds=60)
async def loop():
    global sts, bts

    now_time = dt.now().strftime('%H:%M')

    if now_time == "21:00":
        sts = setting_time_set()
        bts = body_temperature_set()

        em = Embed(title="Notice: Send Time Updated", color=Colour.blue())
        em.add_field(name="Next scheduled send time", value=sts)
        em.add_field(name="Temperature to be sent", value=bts)
        em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
        await client.get_channel(ch).send("<@343956207754805251>")
        await client.get_channel(ch).send(embed=em)
        return

    elif now_time != sts:
        return

    await send(bts=bts)


loop.start()
client.run(os.environ['TOKEN'])

It's going to be very rough, but I'll break it down and explain it in sections.

First, the imports. I've added comments to describe what each library is for.

import json  # Library for handling JSON files (in this case, cfg.json)
import os.path  # Library used for specifying file paths
import random  # Library used for randomly choosing the body temperature later
from datetime import datetime as dt  # Library used for determining the send time

import requests  # Library used for sending requests to Google Forms
from discord import Client, Embed, Colour  # Library for using Discord
from discord.ext import tasks  # Library related to discord.py
from dotenv import load_dotenv  # Library used for loading environment variables from a file

Next.

client = Client()  # Standard boilerplate
ch = CHANNEL_ID # Specify the channel ID where you want to send logs
load_dotenv()  # Load and apply environment variables from the configuration file

From here, I will explain properly.
This function generates a specific transmission time randomly and returns it.
In this case, it is set between 6:00 and 8:30, and I've used an if statement to restrict the minutes to 0-30 only when the hour is 8.
Also, the :02 in return str(f"{setting_time_h:02}:{setting_time_m:02}") is used to pad single digits with a leading zero to ensure they are two digits, making subsequent processing easier.
ex) 9 → 09 / 3 → 03

def setting_time_set():
    setting_time_h = random.randint(6, 8)  # Randomly select between 6 and 8
    if setting_time_h == 8:  # Handle the case for 8
        setting_time_m = random.randint(0, 30)  # Set between 0 and 30
    else:  # For other hours
        setting_time_m = random.randint(0, 59)  # Set between 0 and 59

    return str(f"{setting_time_h:02}:{setting_time_m:02}")

This part is pretty straightforward.
It just randomly picks a temperature for the submission.

def body_temperature_set():
    return random.choice(["36.4", "36.5", "36.6"])

Next.
It wouldn't be an exaggeration to say this is the main part of the program.
I'll explain it to some extent, and for the rest, I'll add comments.
I wrote this quite a while ago, so there are many parts I don't fully understand myself, but please bear with me.

async def send(bts):
    setting_file_path = "/home/seiwell/DiscordBot/Auto-Google-Forms/cfg.json"  # Specify the configuration file path
    with open(setting_file_path, "r", encoding="utf-8")as f:  # Open the file
        cfg = json.load(f)  # Load the JSON file
        cfg["output"]["ans_1"] = f"{dt.now().strftime('%Y-%m-%d')}"

        cfg["output"]["ans_3"] = f"{bts}"

        params = {"entry.{}".format(cfg["entry"][k]): cfg["output"][k] for k in cfg["entry"].keys()}  # Try your best to decipher this.
        res = requests.get(cfg["form_url"] + "formResponse", params=params)  # Send

        if res.status_code == 200:  # If sent successfully, send to a specific Discord channel
            em = Embed(title="Log Info", description=f"[URL]({res.url})", color=Colour.green())
            em.add_field(name="Temperature Info", value=bts)
            em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
            await client.get_channel(ch).send("<@343956207754805251>")
            await client.get_channel(ch).send(embed=em)

        else:  # If some error occurs
            res.raise_for_status()
            em = Embed(title="Log Info", description="Could not send successfully.", color=Colour.red())
            em.add_field(name="Temperature Info", value=bts)
            em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
            await client.get_channel(ch).send("<@343956207754805251>")
            await client.get_channel(ch).send(embed=em)

sts = setting_time_set()   # I don't even know why I'm putting this in a variable here.
bts = body_temperature_set()

I'll explain these parts together.
When the program runs, it notifies a specific Discord channel of the next scheduled send time and temperature.
The next part is something I made so I could send it immediately with a command if I wanted to, but I've actually never used it even once...

@client.event
async def on_ready():
    em = Embed(title="Startup Log", color=Colour.orange())
    em.add_field(name="Next scheduled send date/time", value=f"{sts}")
    em.add_field(name="Temperature to be sent", value=f"{bts}")
    em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
    await client.get_channel(ch).send("<@343956207754805251>")
    await client.get_channel(ch).send(embed=em)


@client.event
async def on_message(message):
    if message.content == "/now_send":
        await send(bts=body_temperature_set())
        await message.channel.send(f"{message.author.mention}-> Send complete")

Next is the second most important part.
Here, I use tasks.loop() to check every 60 seconds whether it's the send time or the reset time.

@tasks.loop(seconds=60)
async def loop():
    global sts, bts

    now_time = dt.now().strftime('%H:%M')

    if now_time == "21:00":  # Reset time check
        sts = setting_time_set()  # Reset process
        bts = body_temperature_set()  # Reset process

        em = Embed(title="Notice: Send Time Updated", color=Colour.blue())
        em.add_field(name="Next scheduled send time", value=sts)
        em.add_field(name="Temperature to be sent", value=bts)
        em.set_footer(text=f'{dt.now().strftime("%Y-%m-%d-%H:%M")}')
        await client.get_channel(ch).send("<@343956207754805251>")
        await client.get_channel(ch).send(embed=em)
        return

    elif now_time != sts:  # Return if it's neither send time nor reset time
        return

    await send(bts=bts)  # Otherwise


loop.start()  # Start the loop process
client.run(os.environ['TOKEN'])  # Start the Discord Bot

Conclusion

What did you think? I tried to explain it without going too deep or staying too shallow.
I know the code is quite messy, but I hope you'll overlook that...

References

Winning against crappy surveys with a Google Form auto-responder Bot (Python3)

Discussion