Nice to meet you. My name is Yourein. Since it is a self-introduction, I will post various things below.
Birthplace: Hokkaido (still resident) Who are you ?: I'm an active industrial high school student What is your programming experience?: I learned the basics of C language in class. I used to teach myself C ++, but recently I'm using Python.
I have some experience with programs, but until now I have been doing very small-scale development for myself that I would never post to sites such as Qiita. The main reason I decided to write this article this time is "I wrote a level of code that I have never experienced before." "I stumbled in various places like a beginner, so I wanted to put together a memorandum for myself."
I decided to pull some code from the program I wrote and post it, but since I am a beginner, I would be grateful if you could overlook it even if you wrote it unsightly.
Although it is labeled as VTuber notification bot, it is actually more correct this time to use holobox notification bot. The goal is to make a bot that behaves like Hololive Notice Server ** by yourself **. This is not a live broadcast notification, but you can think of it as an image. The functional requirements are as follows:
--Notify the latest video or the latest live broadcast of the specified river. --Get some of the latest videos of the specified river. --List and notify the live broadcasts currently being delivered by the box rivers. --Several other features
Actually, I used to create a Python application that uses the Spotify API to get album information and song information, but I couldn't understand the mechanism of Auth at all. I was thinking of using the Youtube Data API this time as well, but it seemed necessary to create several projects due to my weakness in Auth and the number of rivers, so this time I will collect information etc. without using the Youtube Data API. I became a bot.
OS : Windows10 Pro(1909 English(System Locale:Japan)) Editor: ** Visual Studio Code ** Execution environment: ** python ** (The created .py file is opened and used as it is.)/** Discord **
The functions of HoloTools are now available as APIs. See the HoloFans API link for more information
-** Youtube (RSS feed) **
https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID
You can get the xml of the video page of the Youtube channel by doing>. Scraping is prohibited on Youtube, but getting RSS feeds is cool, so I'm eager to make tea muddy.
- **Discord.py**
> Needless to say, this is a well-known library for Discord. I think 50% of the code I wrote this time depends on this.
# Preparation stage
There was the word ** "specified river" ** in the functional requirements mentioned earlier.
The bot created this time does not notify everyone for the time being, but to a bot that has a function such as ** If you specify "I want notification of this river!", It will start notification of that river ** Become.
Prepare a database in JSON with specifiers that specify the river and other information.
It's a good idea to fork the database from Git Hub [because it's in the HoloFans API](https://github.com/holofans/holoapi/blob/develop/database/seeders/json/hololive.json) with more information (packed), but I haven't written JSON myself. I decided to write it after studying.
#### **`members.json`**
```json
{
"counts": 56,
"channels":[
{
"id": 1,
"yt_id": "UCp6993wxpyDPHUpavwDFqgg",
"icon": "https://yt3.ggpht.com/ytc/AAUvwngZmr_qbKhGIvHaHwLRmKhKxdeFfM7ZbK316vFNSw=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "tokino_sora",
"ch_name": "SoraCh.Tokino Sora Channel",
"name": "Tokino Sora",
"specifier": "Sora",
"part": "hololive",
"color": "0x4D84E8"
},
{
"id": 2,
"yt_id": "UCDqI2jOz0weumE8s7paEk6g",
"icon": "https://yt3.ggpht.com/ytc/AAUvwnjiwtRNYHIo1zl37gOIiEeOh-2s7HUdhv6WB8M1vQ=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "robocosan",
"ch_name": "Roboco Ch. -Roboco",
"name": "Roboco-san",
"specifier": "Roboco",
"part": "hololive",
"color": "0xEA00EB"
},
{
"id": 3,
"yt_id": "UC5CwaMl1eIgY8h02uZw7u8A",
"icon": "https://yt3.ggpht.com/ytc/AAUvwnjdAl5rn3IjWzl55_0-skvKced7znPZRuPC5xLB=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "suisei_hosimati",
"ch_name": "Suisei Channel",
"name": "Suisei Hoshimachi",
"specifier": "Suisei",
"part": "hololive",
"color": "0x4D84E8"
},
...
{
"id": 54,
"yt_id": "UCWsfcksUUpoEvhia0_ut0bA",
"icon": "https://yt3.ggpht.com/ytc/AAUvwnjwFEptYg7ed7Ze1nWT7Bj4bbXiOoNwzeM9-4g=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "holostarstv",
"ch_name": "Holosters Official",
"name": "Holosters",
"specifier": "Holostars",
"part":"Holostars 1st-gen",
"color":"0x7979EF"
},
{
"id": 55,
"yt_id": "UCfrWoRGlawPQDQxxeIDRP0Q",
"icon": "https://yt3.ggpht.com/ytc/AAUvwnh523hdzQe8vPD2Du77mqxianT1HHR1McSLHXK4=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "hololive_Id",
"ch_name": "hololive Indonesia",
"name": "hololive Indonesia",
"specifier": "Hololiveid",
"part":"hololiveID 1st-gen",
"color":"0xFF9800"
},
{
"id": 56,
"yt_id": "UCotXwY6s8pWmuWd_snKYjhg",
"icon": "https://yt3.ggpht.com/ytc/AAUvwnieM4gqtwmRtapt0va5VTi7BiKHhsYMxOu9qYRR=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "hololive_En",
"ch_name": "hololive English",
"name": "hololive English",
"specifier": "Hololiveen",
"part":"HololiveEN 1st-gen",
"color":"0xFF0000"
}
]
}
Since JSON is defined in this way, if you rewrite the "counts" that was in the beginning and then add data to JSON, this database can be modified according to future member subscriptions. (** However, since there is code that depends on the database on the HoloFans API side this time, there are functions that can not be used in this program when the service of the API ends. ** The HoloFans API can be deployed even in the local environment for the time being. If you are deploying locally, you can rewrite the API database by yourself, so you can continue to use it even after the service ends. ** This time it is troublesome, but ... ** )
variables
clientid = "CLIENTID"
client = commands.Bot(command_prefix = '.')
thumbnail_endpoint_1 = "https://i.ytimg.com/vi/"
thumbnail_endpoint_2 = "/maxresdefault.jpg "
stream_endpoint = "https://www.youtube.com/watch?v=" #Youtube video page
dischannel = CHANNELID(int) #Discord's channel id
members = json.load(open("members.json", mode = 'r', encoding = "utf-8")) #load hololive members dic
xml_endpoint = "https://www.youtube.com/feeds/videos.xml?channel_id=" #Youtube video feed xml
I put variables that I used many times while writing a program and command_prefix that is necessary for execution in the first place.
In the end, it looks like this. You can get this list with **. Listlive **.
listlive
@client.command()
async def listlive(ctx): #make list of nowlive and return it.
"""Make a list of online-stream hosted by hololive & stars members"""
templist = Holo.apilistlive() #requesting live list to Holoapi
tempnum = len(templist) #getting range of the list
embed_tosend = discord.Embed(title = f"{tempnum}streams on live!")
#sending process
for i in range(tempnum):
embed_tosend.add_field(name = f"{templist[i][1]}'s stream", value = f"[{templist[i][0]}]({stream_endpoint}{templist[i][2]})")
await ctx.send(embed = embed_tosend)
apilistlive
class holoapi(object):
holoapi_endpoint = "https://api.holotools.app/v1/"
def apilivenum(self): #get the number of nowlive
...
def apilistlive(self): #make a list of nowlive
lookup_url = f"{self.holoapi_endpoint}live"
result = requests.get(lookup_url)
live_dic = result.json()["live"]
livelist = []
for i in range(len(live_dic)):
#get each stream information except desc. and some stats.
temp = [live_dic[i]["title"], live_dic[i]["channel"]["name"], live_dic[i]["yt_video_key"]]
livelist.append(temp)
return livelist
https://api.holotools.app/v1/live
You can get the frame information currently being distributed among the rivers registered in the HoloFans API with. If you hit the API, it will come out as JSON, so convert it to a dictionary type, process it, make it a two-dimensional list, and then return the list to the function executed when the command **. Listlive ** is called with return ..
In the for statement, add a field to Embed for the frame currently being delivered, and describe ** "who is delivering", "what is the delivery title", "delivery URL" **, and describe everything. I'm sending it when I'm done. The reason for inline display is ** I didn't know how to disable inline display when I wrote this code **, but when I tried inline disabling after that ** 5 or more was delivered Because the readability at that time was clearly low **. This is probably less readable from a smartphone, but it's okay because I'm only planning to see it on a PC in the first place. (Because it may be open to someone ...)
Next is the function for viewing the archive exclusively. This time, we will get the latest videos of the specified rivers and 5 live distributions. ** This is a type of function that does not depend on API unlike the previous one, so if you update * members.json * (database prepared this time), you can get it by other than the rivers in the holo box. ** ** You don't have to be a river.
This is like this. It would have been nice to send the videos one by one to the text channel with thumbnails, but since it's usually hard to see and disturbing, I obediently put them together in one Embed with inline invalidation.
The command is
.recent specifier(specifier)
is.
recent
@client.command()
async def recent(ctx, spec = "Default"):
"""Make the list of their recent 5 vids or streams and back"""
profile_base = getprofile_withspec(spec)
if profile_base == 0:
embed_tosend = discord.Embed(title="Please input specifier!", descprition = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
elif profile_base == 1:
embed_tosend = discord.Embed(title = "Wrong specifier!", description = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
else:
youtube_name = profile_base["ch_name"]
youtube_id = profile_base["yt_id"]
channel_videopage = f"https://www.youtube.com/channel/{youtube_id}/videos"
channel_xml = feedparser.parse(f"{xml_endpoint}{youtube_id}")
embedcolor = profile_base["color"]
if embedcolor != "undefined":
embedcolor = int(embedcolor, 16)
else:
embedcolor = 0x19FFFF
embed_tosend = discord.Embed(title = f"Recent video of {youtube_name}", url = channel_videopage, color = embedcolor)
for i in range (5):
title = channel_xml["entries"][i]["title"]
title = f"**{title}**"
videoid = channel_xml["entries"][0]["id"]
videoid = videoid.lstrip("yt:video:")
video_page = f"{stream_endpoint}{videoid}"
embed_tosend.add_field(name = title, value = f"Click [here]({video_page})towatch!",inline=False)
await ctx.send(embed = embed_tosend)
getprofile_withspec
def getprofile_withspec(temp):
temp = temp.capitalize()
if temp == "Default":
return 0
else:
for i in range(members["counts"]):
if temp == members["channels"][i]["specifier"]:
return members["channels"][i]
return 1
It may be said that I couldn't devise the return of the function getprofile, but I have only this ability. Please understand. In order to make the specifier case insensitive, we define only the first letter in JSON as uppercase and the others as lowercase and capitalize it in getprofile.
If the specifier is found in JSON, the part where the information of that river is stored will be returned as it is. This time
.recent aqua
Since it was specified in, ** specifier * "aqua" * = Minato Aqua's information should be returned. ** **
aqua
{
"id": 11,
"yt_id": "UC1opHUrw8rvnsadT-iGp7Cg",
"icon": "https://yt3.ggpht.com/ytc/AAUvwngM9Jmc29dvbOY43w7RWFbOZLU4tGtOkEwtt-g7PA=s800-c-k-c0x00ffffff-no-rj",
"tw_id": "minatoaqua",
"ch_name": "Aqua Ch.Minato Aqua",
"name": "Minato Aqua",
"specifier": "Aqua",
"part":"2nd-gen",
"color": "0xFA99E0"
},
The channel ID of Youtube is extracted from the returned information, and the xml of the video feed of the channel is acquired by feedparser. I will omit the explanation of xml, but ** Since each video is registered as an entry, data will be retrieved from xml in a dictionary type. ** ** Since we will get 5 videos this time, we will process the information obtained by turning the loop 5 times, add it to Embed by inline invalidation, and then send it.
Also, if the color information used by the river during live performance is registered ("color" = hexnum), the embed will be qualified with that color and sent. If not, all will be sent in light blue. This time, ** 0xFA99E0 ** is registered in the ** key "color" **, so Embed is qualified with a color close to Light Pink. The theme color is published on the unofficial Wiki for Nijisanji and so on, so you can copy and paste from there, but since there is no such thing with hololive, at the time of the last 2nd fes. Beyond the Stage I used the color picker function of Power Toys to get it from the published image.
[ # Going over Holo Live Goods Notice]
& mdash; Hololive Production [Official](@hololivetv) December 11, 2020
Because there were many requests and inquiries from everyone,
This time the goods reorder sales period It will start one week ahead of schedule on December 14th at 0:00 AM.
▼ Goods purchase/details https://t.co/VstXwaGzzi pic.twitter.com/FtV7XXsgC1
** From here on, I rely on the power of the f-string. It can't be helped. .. .. It's convenient. .. .. ** **
** To be honest, I was stepping on that it seemed to be the easiest, but I was really stuck here. ** ** Since it does not use the YoutubeDataAPI, it does not go to real-time update, but by updating the xml feed every few minutes, guerrilla delivery etc. can also be obtained. In the first place, I don't know how often the xml feed is updated, so I don't know until I actually operate it.
Since the functional requirement was to ** deliver only the notifications of the rivers that requested the notifications **, we will start by implementing the function to manage the rivers that notify the notifications. ** **
editclist
@client.command()
async def editclist(ctx, mode = "add", spec = "Default"):
"""Add or delete members on checklist"""
check = 0
if mode == "add":
check = RCh.addlist(spec)
if check == '0':
embed_tosend = discord.Embed(title="Please input specifier!", descprition = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
elif check == '1':
embed_tosend = discord.Embed(title = "Wrong specifier!", description = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
elif check == '2':
embed_tosend = discord.Embed(title="Success!", description="Member you chose was successfully added to checklist!")
await ctx.send(embed = embed_tosend)
elif mode == "delete":
check = RCh.dellist(spec)
if check == '0':
embed_tosend = discord.Embed(title="Please input specifier!", descprition = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
elif check == '1':
embed_tosend = discord.Embed(title = "Wrong specifier!", description = "Check specifier with [.spechelp] command!")
embed_tosend.set_image(url = "https://i.ytimg.com/vi/7tQgpXgv570/maxresdefault.jpg ")
await ctx.send(embed = embed_tosend)
elif check == '2':
embed_tosend = discord.Embed(title="Success!", description="Member you chose was successfully deleted from checklist!")
await ctx.send(embed = embed_tosend)
elif mode == "check":
RCh.checklist_debug()
await ctx.send("**Check the console!**")
recentchecking1
class recentchecking(object):
check_list = []
def addlist(self, temp):
temp = temp.capitalize()
if temp == "Default":
return '0'
else:
for i in range(members["counts"]):
if temp == members["channels"][i]["specifier"]:
self.check_list.append([members["channels"][i]["name"], members["channels"][i]["yt_id"]])
return '2'
return '1'
def dellist(self, temp):
temp = temp.capitalize()
if temp == "Default":
return "0"
else:
for i in range(members["counts"]):
if temp == members["channels"][i]["specifier"]:
self.check_list.remove([members["channels"][i]["name"], members["channels"][i]["yt_id"]])
return "2"
return "1"
def checklist_debug(self):
print(self.check_list)
This is an on-parade code for nesting if statements, but here we manage notification requests.
The description ** RCh.function name ** appears several times, but the variable RCh refers to class recent checking. Regarding the ** return returned from the class, it is the same as before, 0, 1 is an error, 2 is a normal end. ** **
.editclist mode specifier
You can check ** "Add notification request river", "Delete", "Who is registered now" **. When a river is added to the list, a notification like the one above will fly to Discord. If you check the contents of the current list here,
checklist
[["Minato Aqua", "UC1opHUrw8rvnsadT-iGp7Cg"]]
It will be. We will use this data to obtain information.
recentchecking2
class recentchecking(object):
check_list = []
recent_list = []
old_recent_list = []
def getmostrecent(self):
self.recent_list.clear() #Reset recent_list for running
for i in range(len(RCh.check_list)): #Add one's recent video to recent_list
youtube_id = self.check_list[i][1]
channel_xml = feedparser.parse(f"{xml_endpoint}{youtube_id}")
recent_video_title = channel_xml["entries"][0]["title"]
recent_video_id = channel_xml["entries"][0]["yt_videoid"]
#check video or stream status
returned = Holo.checkstatus(recent_video_id)
status = returned[0]
scheduled_time = returned[1]
#embed_color
embedcolor = getprofile_withytid(youtube_id)
embedcolor = embedcolor["color"]
if embedcolor != "undefined":
embedcolor = int(embedcolor, 16)
else:
embedcolor = 0x19FFFF
#appends latest stream information
self.recent_list.append([recent_video_title, recent_video_id, status, scheduled_time, embedcolor, False])
def compareon(self):
if len(self.old_recent_list) < len(self.recent_list):
for i in range(len(self.old_recent_list), len(self.recent_list)):
self.recent_list[i][5] = True
for i in range(len(self.old_recent_list)):
if (self.old_recent_list[i][1] != self.recent_list[i][1]) or (self.old_recent_list[i][2] != self.recent_list[i][2]):
self.recent_list[i][5] = True
def checking(self):
self.getmostrecent()
self.compareon()
self.old_recent_list = copy.deepcopy(self.recent_list)
Since the code block itself is long, I separated it separately, but in reality, the checklist addition (deletion) part and the above code block belong to the same class. What you are doing with the getmostrecent function is not much different from fetching the previous 5 videos. ** Get yt_id stored in checklist and get xml. ** ** From there, I get only the latest videos as ** xml ["entries"] [0] **. The acquired ** information is processed and stored in recent_list. ** **
The contents of recent_list are as follows, and the data sent to Discord is determined based on the contents of this list. Also, since I am updating recent_list with ** append, recent_list must be empty every time it is executed **, so ** self.recent_list.clear () ** should be the first in getmostrecent. I'm writing and resetting recent_list.
recent_list
[["Video title", "Video ID(For URL molding that jumps to the viewing page)", "Video status(live, past, upcoming)", "Delivery start time(UTC+09)"(Stored only when the state is live or upcoming),"Bool flag"(Flag to send to Discord. Send if True)]]
Also, I'm hitting the Holofans API to get the status of the video, and at that time I also list the delivery start time and have it returned to the check function Holo.checkstatus.
checkstatus
def checkstatus(self, temp):
lookup_url = f"{self.holoapi_endpoint}{self.holoapi_video_endpoint}{temp}"
result = requests.get(lookup_url)
video_dic = result.json()
if video_dic["status"] == "live":
st = video_dic["live_schedule"]
st = st.replace('T', ' ')
st = st.replace('Z', '')
st = st.replace('.000', '')
starttime = replace_JST(st)
return_list = [video_dic["status"], starttime]
print(return_list)
return return_list
elif video_dic["status"] == "past":
return_list = [video_dic["status"], ""]
print(return_list)
return return_list
elif video_dic["status"] == "upcoming":
st = video_dic["live_schedule"]
st = st.replace('T', ' ')
st = st.replace('Z', '')
st = st.replace('.000', '')
starttime = replace_JST(st)
return_list = [video_dic["status"], starttime]
print(return_list)
return return_list
replace_JST
#k0gane_p(@k0gane_p)I am using the function created by.
#The processing here is quite@k0gane_I have linked the article to the end of this section because there is a part that referred to Mr. p's article.
def replace_JST(s):
a = s.split("-")
u = a[2].split(" ")
t = u[1].split(":")
time = [int(a[0]), int(a[1]), int(u[0]), int(t[0]), int(t[1]), int(t[2])]
if(time[3] >= 15):
time[2] += 1
time[3] = time[3] + 9 - 24
else:
time[3] += 9
return (str(time[0]) + "/" + str(time[1]).zfill(2) + "/" + str(time[2]).zfill(2) + " " + str(time[3]).zfill(2) + ":" + str(time[4]).zfill(2))
** The function compareon selects the video to actually send to Discord. ** ** Compare the old_recent_list (I don't know the name ...) that stores the data acquired the previous time with the newly acquired recent_list, and operate the Bool flag that determines whether or not to send.
if len(self.old_recent_list) < len(self.recent_list):
if len(self.old_recent_list) < len(self.recent_list):
for i in range(len(self.old_recent_list), len(self.recent_list)):
self.recent_list[i][5] = True
for i in range(len(self.old_recent_list)):
if (self.old_recent_list[i][1] != self.recent_list[i][1]) or (self.old_recent_list[i][2] != self.recent_list[i][2]):
self.recent_list[i][5] = True
Here, ** when content that is not in old_recent_list is added to recent_list ** (when a new notification request river is added or when the bot is launched and executed for the first time), ** all the content that is not in old_recent_list It is set to send. ** **
In the next for statement, the old_recent_list and recent_list are simply compared, and when a new frame or video is found, the contents are sent to Discord. Also, even in the same frame, notifications will be sent three times before distribution, after the start, and after the end (after the archive is released).
So, since it is necessary to execute the process regularly, we adopted the fixed time loop in the extension function of Discord.py. ** (There is also a reference article here, so I will link it together at the end of the section.) **
loop
@tasks.loop(seconds=35)
async def loop():
now = datetime.now().strftime('%M')
if (int(now) % 30) == 0: #Do this every %n minutes
print("Doing")
channel = client.get_channel(dischannel)
if RCh.check_list != []:
RCh.checking()
print(RCh.recent_list)
for i in range(len(RCh.recent_list)):
if RCh.recent_list[i][5] == True:
if RCh.recent_list[i][2] == "live":
video_page = f"{stream_endpoint}{RCh.recent_list[i][1]}"
video_name = RCh.recent_list[i][0]
embed_tosend = discord.Embed(title=f"{RCh.check_list[i][0]} now streaming!", description = f"[**{video_name}**]({video_page})",color=RCh.recent_list[i][4])
embed_tosend.add_field(name = "Start Time", value = RCh.recent_list[i][3])
embed_tosend.add_field(name = "Video ID", value = RCh.recent_list[i][1])
embed_tosend.set_image(url = f"{thumbnail_endpoint_1}{RCh.recent_list[i][1]}{thumbnail_endpoint_2}")
elif RCh.recent_list[i][2] == "past":
video_page = f"{stream_endpoint}{RCh.recent_list[i][1]}"
video_name = RCh.recent_list[i][0]
embed_tosend = discord.Embed(title=f"{RCh.check_list[i][0]}'s new video!", description = f"[**{video_name}**]({video_page})",color=RCh.recent_list[i][4])
embed_tosend.add_field(name = "Video ID", value = RCh.recent_list[i][1])
embed_tosend.set_image(url = f"{thumbnail_endpoint_1}{RCh.recent_list[i][1]}{thumbnail_endpoint_2}")
elif RCh.recent_list[i][2] == "upcoming":
video_page = f"{stream_endpoint}{RCh.recent_list[i][1]}"
video_name = RCh.recent_list[i][0]
embed_tosend = discord.Embed(title=f"{RCh.check_list[i][0]}'s future stream!", description = f"[**{video_name}**]({video_page})",color=RCh.recent_list[i][4])
embed_tosend.add_field(name = "Estimated Start Time", value = RCh.recent_list[i][3])
embed_tosend.add_field(name = "Video ID", value = RCh.recent_list[i][1])
embed_tosend.set_image(url = f"{thumbnail_endpoint_1}{RCh.recent_list[i][1]}{thumbnail_endpoint_2}")
await channel.send(embed = embed_tosend)
** At this point, the data that has been molded so far is finally transmitted. ** ** ** Get the current time (minutes only) every 35 seconds, request xml when that minute is divisible by 30, and start the process so far. ** The point is that the process is executed every 30 minutes.
By the way, if you check the time every 35 seconds, when the bot starts executing at Nh hours Nm minutes 00 seconds, if it is every 30 seconds, the same process will be repeated twice a minute. It's usually useless, so I set it to 35 seconds to prevent it. (If so, I will answer "I want information as soon as possible" to the opinion that 60 is okay.)
I prepared some functions so far, but the ** function that is actually called ** is a completely different function called ** RCh.Recent_list (). ** ** Insanely simple
RCh.recent_list
def checking(self):
self.getmostrecent()
self.compareon()
self.old_recent_list = copy.deepcopy(self.recent_list)
** That's all, but here I got stuck for 5 hours **
The reason is that I didn't know the reference copy and the actual copy of python, but at first I wrote as follows.
Wrongcode
self.old_recent_list = self.recent_list
I thought that I could copy the contents of the list with this, but apparently ** old_recent_list was assigned something like a pointer of recent_list and it seemed to be copied in a pseudo manner, but in reality both of them have the same entity. It is a punch line that was represented **.
** In C ++, even structures can be copied with =, so I was completely interested in it. ** **
** I wish I had noticed it sooner, but sooner or later I was grateful that I would have been fighting a fundamental mistake that I couldn't understand for a longer time without searching the Web. ** **
If executed successfully, the following notification will be sent.
The problem is that a notification is always sent at the first execution after registering a river, but since it is intended to be deployed on a mackerel machine and then executed all the time, there is no big problem. I think. My house often trips, so I'm likely to end up seeing the same notification several times.
Python: Making a Discord Bot (Rewrite / v1.x)
Many other sites, videos, etc. All of them were very helpful. Thank you very much.
--Code optimization
I think it's a poor code myself. So I'm thinking of deleting unnecessary variables or changing the syntax.
--Addition of functions
Since it is still a Ver1.2 bot, I feel that it is necessary to add functions in the future. It's just a notification bot, so you don't have to be so enthusiastic.
--Rewrite to code that does not depend on HoloTools
This is the ultimate goal of the roadmap for the future. To achieve this, you need to understand how to use the Youtube Data API, and it is expected that the level will rise dramatically from this time. However, I would like to take on the challenge in consideration of the future.
If this can be done, not only hololive but also information such as the rivers of the outer box can be acquired, so the degree of freedom of the bot as a whole will increase. Currently, I am running the name of this bot as "Holo checker", but I am aiming to be able to run it as "Vcheker" someday.
Currently, the number of lines at the time of writing this article is 500 (1100 including the JSON I made), so it is considerably shorter than a general program, but in the first place it is a condition that it is on that scale and will continue to be used. It was a very fresh experience as I had never developed it below.
It's the same for both programs and studies, but I have to do it, so I can't remember it, so by building this program this time, I became able to see Python, and I feel that I've finally become able to handle the language Python in earnest . ( I agree with the opinion, "Isn't it practical?" **)
It's a bot I made myself, so I'll keep going with it for a long time. Perhaps the contents will change completely in a few months or years ...
** Finally, it was a very long article, but thank you for reading this far. ** ** I hope this article is helpful for you.
Recommended Posts