mirror of
https://github.com/arthur-pbty/xiao.git
synced 2026-06-03 15:07:42 +02:00
Remove 15 commands, patreon and bot list stuff
This commit is contained in:
@@ -5,19 +5,10 @@ OWNERS=
|
||||
LOVER_USER_ID=
|
||||
XIAO_PREFIX=
|
||||
INVITE=
|
||||
XIAO_WEBHOOK_ID=
|
||||
XIAO_WEBHOOK_TOKEN=
|
||||
REPORT_CHANNEL_ID=
|
||||
JOIN_LEAVE_CHANNEL_ID=
|
||||
COMMAND_CHANNEL_ID=
|
||||
|
||||
# Bot list tokens
|
||||
TOP_GG_TOKEN=
|
||||
BOTS_GG_TOKEN=
|
||||
DISCORDBOTLIST_TOKEN=
|
||||
CARBON_TOKEN=
|
||||
BLIST_TOKEN=
|
||||
|
||||
# Redis info
|
||||
REDIS_HOST=
|
||||
REDIS_PASS=
|
||||
@@ -62,8 +53,6 @@ GOV_KEY=
|
||||
IMGUR_KEY=
|
||||
OPENWEATHERMAP_KEY=
|
||||
OSU_KEY=
|
||||
PATREON_ACCESS_TOKEN=
|
||||
PATREON_CAMPAIGN_ID=
|
||||
PERSONAL_GOOGLE_CALENDAR_ID=
|
||||
SPOTIFY_KEY=
|
||||
SPOTIFY_SECRET=
|
||||
|
||||
@@ -16,7 +16,6 @@ config.json
|
||||
command-leaderboard.json
|
||||
command-last-run.json
|
||||
blacklist.json
|
||||
patreon.json
|
||||
|
||||
# Tensorflow Models
|
||||
tf_models/
|
||||
|
||||
@@ -53,7 +53,6 @@ client.registry
|
||||
['edit-image', 'Image Manipulation'],
|
||||
['edit-image-text', 'Image Text Manipulation'],
|
||||
['edit-avatar', 'Avatar Manipulation'],
|
||||
['edit-face', 'Face Manipulation'],
|
||||
['edit-meme', 'Meme Generators'],
|
||||
['edit-text', 'Text Manipulation'],
|
||||
['edit-number', 'Number Manipulation'],
|
||||
@@ -128,18 +127,6 @@ client.on('ready', async () => {
|
||||
}
|
||||
}, 1.8e+6);
|
||||
|
||||
// Import forced patrons
|
||||
try {
|
||||
const results = client.patreon.importForced();
|
||||
if (results) {
|
||||
client.logger.info('[PATREON] patreon.json successfully loaded.');
|
||||
} else {
|
||||
client.logger.error('[PATREON] patreon.json is not formatted correctly.');
|
||||
}
|
||||
} catch (err) {
|
||||
client.logger.error(`[PATREON] Could not parse patreon.json:\n${err.stack}`);
|
||||
}
|
||||
|
||||
// Import blacklist
|
||||
try {
|
||||
const results = client.importBlacklist();
|
||||
@@ -222,33 +209,6 @@ client.on('ready', async () => {
|
||||
client.logger.error(`[NSFW MODEL] Failed to load NSFW model\n${err.stack}`);
|
||||
}
|
||||
|
||||
// Import Patrons
|
||||
try {
|
||||
await client.patreon.fetchPatrons();
|
||||
setInterval(() => {
|
||||
client.patreon.fetchPatrons()
|
||||
.then(() => client.logger.info('[PATREON] Updated patron list.'))
|
||||
.catch(err => client.logger.error(`[PATREON] Failed to update patron list:\n${err.stack}`));
|
||||
}, 3.6e+6);
|
||||
client.logger.info('[PATREON] Fetched patrons.');
|
||||
} catch (err) {
|
||||
client.logger.error(`[PATREON] Failed to fetch patrons:\n${err.stack}`);
|
||||
}
|
||||
|
||||
// Post bot list stats
|
||||
await client.botList.postTopGGStats();
|
||||
await client.botList.postBotsGGStats();
|
||||
await client.botList.postDiscordBotListStats();
|
||||
await client.botList.postCarbonStats();
|
||||
await client.botList.postBlistStats();
|
||||
setInterval(() => {
|
||||
client.botList.postTopGGStats();
|
||||
client.botList.postBotsGGStats();
|
||||
client.botList.postDiscordBotListStats();
|
||||
client.botList.postCarbonStats();
|
||||
client.botList.postBlistStats();
|
||||
}, 1.8e+6);
|
||||
|
||||
// Fetch all members
|
||||
for (const [id, guild] of client.guilds.cache) { // eslint-disable-line no-unused-vars
|
||||
await guild.members.fetch();
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,37 +0,0 @@
|
||||
-Kino's Journey- A hare's encounter
|
||||
|
||||
"Kino, isn't it strange." The motorrad asked the traveler.
|
||||
|
||||
"What is it, Hermes?" The traveler asked the motorrad.
|
||||
|
||||
"No matter how much of a trail we leave on our journey, no one ever follows us." The dust kicked up behind Hermes' wheels as they drove on their way to nowhere in particular.
|
||||
|
||||
"That's not strange, it's just not true." Kino answered.
|
||||
|
||||
Hermes groaned. "Kino, what do you mean? We've made a lot of enemies on our travels, but I've never seen one behind us."
|
||||
|
||||
Kino just laughed. "You don't get it Hermes, just because you can't see them doesn't mean they aren't there."
|
||||
|
||||
"Whoa, Kino, why'd you have to mention it like that? That's just creepy." Hermes shuddered.
|
||||
|
||||
"Hermes, don't ask questions if you don't want to hear the answer." Kino replied. Just then, they heard a bush rustle. Kino quickly slid down to the ground, hopped off Hermes, and pulled her gun, aiming it at the bush.
|
||||
|
||||
After a few more rustles, a lone hare came out of the bush. Seeing Kino with her weapon, it quickly hopped away. Kino put her gun back in its holster and sighed.
|
||||
|
||||
Hermes laughed. "Guess I'm not the only jumpy one. Now please pick me up."
|
||||
|
||||
Kino smiled. "Nah, I think I'll go on alone. Sorry Hermes." The traveler grabbed her bag from Hermes' rear, and started to walk off.
|
||||
|
||||
"Kino, wait, I'm sorry! Come back." Hermes pleaded, as Kino laughed.
|
||||
|
||||
"Hermes, we've traveled so long together, you really think I'm not messing with you?"
|
||||
|
||||
"How horrible. Who would trick their friend like this? Please pick me up." Hermes continued. Kino finally obliged, righted her partner and hopped onto the annoyed motorrad's seat.
|
||||
|
||||
"One thing you said is true, though." Kino stated, as she twisted Hermes' handles to start his engine.
|
||||
|
||||
"What is it?"
|
||||
|
||||
"What I said was pretty creepy."
|
||||
|
||||
-Prologue "A hare's encounter" end-
|
||||
@@ -1,239 +0,0 @@
|
||||
-Kino's Journey- Country of Embrace
|
||||
|
||||
The dirt kicked up behind the motorrad Hermes' wheels as he and the traveler Kino followed the dirt road to their next destination.
|
||||
|
||||
"To tell you the truth Hermes, this next country has a strange reputation among travelers." Kino told her partner.
|
||||
|
||||
"Really? What do they say?" Hermes asked, curious.
|
||||
|
||||
"Most just say it's strange, though one thing in particular caught my interest." Kino explained. "Someone told me it was a very loving country."
|
||||
|
||||
"Loving? What do you think they mean by that?" Hermes asked.
|
||||
|
||||
"I don't know, they wouldn't say more." Kino then smirked, and remarked, "But that just makes me more eager to arrive."
|
||||
|
||||
Hermes sighed. "Kino, you're going to get us kidnapped someday. I'll be scrapped, and you'll be someone's servant making their breakfast every morning."
|
||||
|
||||
"I doubt that, Hermes." Kino stated, matter-of-factly.
|
||||
|
||||
"Why's that? You seem reckless enough to me."
|
||||
|
||||
"It's not that Hermes." Kino smiled, "I'm just a horrible cook."
|
||||
|
||||
***
|
||||
|
||||
Kino and Hermes soon arrived at the country, it's towering walls dwarfing the two as they stared in awe. Kino walked up to the entrance gate, and the guard spotted them. Strangely, the guard seemed to be wearing very casual attire.
|
||||
|
||||
"Welcome traveler!" The guard exclaimed, greeting them. "And what brings you to our wonderful country?"
|
||||
|
||||
"My name is Kino, and this motorrad here is Hermes." Kino introduced.
|
||||
|
||||
"Hey there." Hermes casually remarked.
|
||||
|
||||
"We're on a journey, and would like to spend 3 days here. Would that be okay?" Kino asked the guard.
|
||||
|
||||
"Most certainly! In fact, 3 days may not be nearly enough for you! Stay as long as you wish!" The guard enthusiastically remarked, in his shrill voice.
|
||||
|
||||
"Thank you, but 3 days will be plenty." Kino stated.
|
||||
|
||||
"Well, as you wish, traveler." The guard bowed, then pulled a large lever. The gate slowly began to lower, and light poured through the cracks. As the gate reached it's lowest, Kino and Hermes gasped at the beauty of the vast country's cityscape.
|
||||
|
||||
"Welcome, traveler." The guard smiled. "To the Country of Embrace."
|
||||
|
||||
***
|
||||
|
||||
Kino and Hermes walked slowly past the large wooden gate, taking in the sight of a wonderfully beautiful city, with a large fountain at its enter and beautiful cottages. Several cherry trees dotted the empty areas, creating a wondrously surreal scene of falling pink petals.
|
||||
|
||||
The entire town seemed to have gathered in the square, welcoming Kino and Hermes with a grand celebration. "Welcome, traveler!" They chanted in unison, causing Kino to feel slightly uneasy.
|
||||
|
||||
"Wow, what a grand welcome! I could get used to this!" Hermes told Kino, happily.
|
||||
|
||||
"I don't know Hermes, this seems rather strange." Kino whispered to her partner. "Last time we got a welcome like this..."
|
||||
|
||||
"Oh lighten up Kino, that won't ever happen again! It's more likely this country is just crazy." Hermes laughed, but Kino remained straight-faced for a moment. Then, she just smiled.
|
||||
|
||||
"Yeah, I guess you're right." Kino popped Hermes' kickstand, let him go and looked towards the people of the country. "Thank you all for the kind welcome, I very much appreciate it. Do any of you know where I can find a place to stay?"
|
||||
|
||||
The crowd quickly roared, as every member of the country seemed to suggest their own house. Kino noticed something strange about the people volunteering, though. They were all men. In fact, on closer inspection, so was the entire crowd. The few women or children that were present were not speaking a peep.
|
||||
|
||||
"I appreciate your hospitality, but I would prefer an inn of some sort. I wouldn't want to impose on any of you." Kino nervously stated, but the entire crowd didn't seem to pay and heed to her words.
|
||||
|
||||
"Kino, why are they all volunteering like this? That seems rather strange." Hermes asked, clearly thinking the same as Kino.
|
||||
|
||||
"I don't know. Frankly, I wouldn't mind too much, but these cottages seem awfully small for a family and myself to stay." Kino noted, looking towards one of the pretty, but tiny houses that surrounded them.
|
||||
|
||||
"Don't worry about that, traveler." One of the men walked up next to them, smiling. "These houses are plenty large enough for you and one man."
|
||||
|
||||
"What? You mean everyone here lives alone?" Kino asked, confused.
|
||||
|
||||
"Yes, traveler. You see, females are scarce here. We rely on travelers immigrating to make sure we can keep our population up for the next generation." The man laughed. "Were you not told that coming in?"
|
||||
|
||||
"No, that piece of info was left out." Kino stated, looking back at the gate. It had since closed, with the guard nowhere to be seen. "And I'm beginning to think that was intentional."
|
||||
|
||||
"Most travelers don't take too kindly to our struggle and turn around, so our guard started to neglect mentioning it to them." The man said, smiling. "I'm very sorry he did that to you, but we cannot allow you to go back on your promise to stay for at least 3 days."
|
||||
|
||||
"Kino, this doesn't seem like a very fair agreement to me." Hermes quipped. Kino just kicked him, and smirked.
|
||||
|
||||
"I see. Well, I suppose I'll be staying here for the next 3 days then." Kino stated, looking towards the crowd. "But I cannot help you with your struggle. I am a traveler, and will remain so."
|
||||
|
||||
"That is very much okay, I do sincerely apologize for the confusion." The man stated, his smile turning to a frown. "Let me make amends by offering my very own home to you as lodgings."
|
||||
|
||||
"I appreciate that, thank you." Kino thanked the man, releasing Hermes' kickstand and holding him up, "I hope you don't mind me bringing Hermes along as well."
|
||||
|
||||
"Not at all, Kino." The man said, smiling again. "Anything you need, please just let me know. My house is the one furthest from the square, across from a similar fountain to this one. You'll recognize it as the pink one labeled ‘Country lead'."
|
||||
|
||||
Hermes gasped, "Kino, what an honor! Staying at the head of the country's house!"
|
||||
|
||||
"Yes, an honor indeed." Kino smiled at the man, who smiled back. But there was only one thing the two's smiles shared.
|
||||
|
||||
They were both fake.
|
||||
|
||||
***
|
||||
|
||||
Kino wheeled Hermes into the cottage, which was larger than most of the houses in the country but still rather small for something as prestigious as the "country lead". It was furnished sparsely, if Kino didn't already know, she'd have taken it for the house of a common man.
|
||||
|
||||
The country lead walked in soon after. "Welcome to my humble abode." He said, merrily. "I hope it's not too small for your tastes, traveler."
|
||||
|
||||
"No, I'm used to staying in inns far smaller than this. It'll be plenty." Kino said, smiling. "Do you live alone here?"
|
||||
|
||||
"Yes, I do. It may seem strange for me to be one of the people in this country with no companions, but it doesn't bother me." The man smiled. "Rather, it's quite a relief."
|
||||
|
||||
"Hmm? In what way is that?" Hermes asked, curiously.
|
||||
|
||||
"Freedom." The man stated, bluntly. "You see, as much as I'd love to share my vast wealth with another, that would limit my ability to do as I wish."
|
||||
|
||||
"I see." Kino said, losing her smile.
|
||||
|
||||
"Does that bother you, traveler?" The man asked.
|
||||
|
||||
"No, not at all." Kino replied, "I'm simply astonished by how selfish one man could be."
|
||||
|
||||
"Well, how rude." The man laughed, sitting down in a nearby chair. "And yet, you're not wrong. Stay as long as you like, traveler. It may be nice to have some company for a change."
|
||||
|
||||
"Kino is very boring, so probably not." Hermes joked. Kino simply let go of his handles, letting him fall to the ground. "How terrible."
|
||||
|
||||
"I'd appreciate if you didn't drop your partner on my floor."
|
||||
|
||||
All of them laughed. Well, except Hermes of course. And unlike last time, these smiles were genuine.
|
||||
|
||||
"Well, I best be on my way. I have to do my rounds as leader." The man said, getting up from his chair and heading to the door.
|
||||
|
||||
Kino picked up Hermes, much to the motorrad's pleasure, and began to follow the man. "Would you mind if we tagged along? I would love to see more of your country."
|
||||
|
||||
"Not at all, I'd be delighted to show you." The man said, smiling. He opened the door, and stepped out of the way. "After you, traveler."
|
||||
|
||||
Kino left the house, wheeling Hermes along with her. "Kino, I'm confused." The motorrad whispered. "I was getting the feeling you were against this country, but now you seem fine with him."
|
||||
|
||||
"First impressions are important Hermes." Kino answered. "But so are second ones."
|
||||
|
||||
***
|
||||
|
||||
"This country was only founded just recently, by a group of traveling merchants." The man explained, as he and Kino walked under the cherry trees that lined the town's plaza. "We wanted to create the perfect country for our trade, and we stumbled upon the ruins of an ancient civilization. In the spirit of recycling, we made it our home."
|
||||
|
||||
"That's an interesting story, but why the lack of women?" Hermes asked.
|
||||
|
||||
"You see, we as merchants didn't bring our wives and children along with us. Many of us never even had such people to begin with. A few went back for their loved ones and brought them here, but most of us are just lonely." The man explained, laughing. "Like me, of course."
|
||||
|
||||
"You're a nice enough guy, I'm surprised at that." Kino said, smiling.
|
||||
|
||||
The man smiled back. "Well, perhaps if I met the right girl. But they'd have to be rather independent, I don't want anyone treading on my private time."
|
||||
|
||||
Kino and the man both laughed, and started to exit the tree garden. They reached the main plaza, where confetti was falling from the sky and bells rang throughout the country. "Ah, I see a marriage is taking place right now, actually." The man said, as they approached a church-like building. Many people had gathered for what looked like, well, a wedding.
|
||||
|
||||
A man and woman stood atop the building's large staircase. Presumably the bride and groom, the two seemed happy, and had their arms linked. The woman wore a beautiful blue dress, whereas her partner was wearing a black tuxedo.
|
||||
|
||||
"Kino, they look happy, but I thought this country kidnapped people to marry." Hermes asked. Kino quickly shushed him for being rude, but the village leader just laughed.
|
||||
|
||||
"Well, that may be true to an extent." The man said. "But anyone who does not wish to partake in our country is free to leave, we simply ask you consider our proposal."
|
||||
|
||||
"That's much more honorable than I initially thought." Kino said, happily. She was mostly just relieved she wouldn't be kidnapped. "I hope you find many travelers to help you grow your beautiful country."
|
||||
|
||||
"As do I." The man said, chuckling nervously. "If you'll excuse me, I probably should go congratulate the two of them. I'll be right back." The man walked away from Kino and Hermes, heading up the steps as the two travelers watched the festivities in awe.
|
||||
|
||||
"I like this idea of the country much more, Kino." Hermes said.
|
||||
|
||||
"You shouldn't." Kino and Hermes quickly turned around to see an older woman, maybe in her sixties, standing behind them. "It's all a farce."
|
||||
|
||||
"A farce, you say? In what way is that?" Kino asked, nervously.
|
||||
|
||||
"This country kidnaps you, makes you love it here, gets you married to one of their perfect men, and then once you have a few kids, abandons you." The woman explained, clearly on the verge of tears. Kino and Hermes, though, were confused.
|
||||
|
||||
"But I thought this country was only founded recently." Hermes asked.
|
||||
|
||||
"It was, technically. Only about 10 years ago." The woman answered.
|
||||
|
||||
"But if the country makes you love it so much you stay, is that really kidnapping?" Kino asked.
|
||||
|
||||
"Yes!" The woman screamed. "It's all a trap! I could've been the greatest traveler in the world if not for this godforsaken hellhole of a country!"
|
||||
|
||||
"Kino!" The village leader yelled, running down the steps. Kino turned to him, but Hermes screamed as well.
|
||||
|
||||
"Kino, dodge! Hurry!" Hermes yelled. Kino quickly jumped out of the way, falling to the ground. As she looked up, she saw the reflection of her face in the steel of a knife, which the mysterious woman had pulled. The village lead quickly took the knife from the woman's hands, and a few guards pinned her to the ground.
|
||||
|
||||
The woman screamed as they struggled to hold her down, "release me!" She cried, "I need to save this child from the agony I went through!"
|
||||
|
||||
"I don't think stabbing is the way to do that." Hermes remarked, as Kino breathed heavily, clearly in shock. "Are you alright Kino?"
|
||||
|
||||
"I'm fine, Hermes." Kino said, taking a deep breath. "That was scary."
|
||||
|
||||
"I'm so very sorry, traveler, this woman is insane. She only just recently was divorced by her husband, and she blames our country for it." The village lead explained. The guards seemed to have gotten control of the situation, and were now carrying the woman away. The crowd of the wedding seemed to still be quiet, but had mostly returned to watching the festivities.
|
||||
|
||||
"It's okay." Kino said, taking another deep breath. "I was worried for a second you were lying to me again."
|
||||
|
||||
"Traveler, you don't need to fear. You will be fully permitted to leave whenever you see fit." The village lead said, smiling. "I hope this didn't give you a poor opinion of our country."
|
||||
|
||||
"Not at all, thank you. I'll be leaving in two days, as scheduled. Until then, please continue to guide me." Kino said, now smiling in relief.
|
||||
|
||||
"As you wish, Kino."
|
||||
|
||||
***
|
||||
|
||||
Two days quickly passed by, and Kino and Hermes spent the rest of their time in the country exploring the nature and beauty of the sights. The village lead guided them, taking them to various hot spots and museums, as well as diners. Kino especially enjoyed the desserts the country offered, and had eaten quite a few, much to the village lead's surprise. It was a fun time, and many laughs were shared.
|
||||
|
||||
Soon, the time for the travelers to depart had arrived, and Kino and Hermes went to the exit gate, accompanied by the village lead.
|
||||
|
||||
"Thank you very much for the hospitality, I really appreciate it. I had a lot of fun." Kino said, as she hopped onto Hermes and started his engine.
|
||||
|
||||
"The pleasure was all mine." The man said, smiling. "At first, I was disappointed as I knew you weren't staying, but I had enough fun to make up for it ."
|
||||
|
||||
"What a couple of lovebirds." Hermes quipped, causing Kino to kick him. "Ouch."
|
||||
|
||||
"You know, that's not such a bad idea." The village head said, causing Hermes to gasp. "What do you say Kino?"
|
||||
|
||||
"I'm sorry, but I am a traveler, and that's what I plan to continue to be." Kino said, "but I'm flattered."
|
||||
|
||||
"Kino, please. Consider it. I will do everything I can to make you happy, far more than a simple journey!" The man pleaded.
|
||||
|
||||
"I'm sorry, I'm not going to stay here." Kino said, bluntly.
|
||||
|
||||
"But Kino, plea-" The man started, but Kino had pulled her gun, and had it aimed squarely at the man's chest.
|
||||
|
||||
"No means no."
|
||||
|
||||
The man finally backed down, sighing. "I wish you good luck then, Kino. I hope to someday see you again."
|
||||
|
||||
Kino placed her gun back in its holster and smiled, took Hermes' kickstand off and rode past the gate.
|
||||
|
||||
***
|
||||
|
||||
"Kino, why did you reject his offer? You could've been a village leader, and he had the hots for you, of all people!" Hermes asked Kino, as the two rode towards nowhere in particular.
|
||||
|
||||
"Hermes, as much as it may have seemed, that man didn't love me. He was simply lonely." Kino explained, "he would have asked the same question to any woman who spent a day having fun with him."
|
||||
|
||||
"What's wrong with that? It would still be great after a bit." Hermes explained. "You had good chemistry."
|
||||
|
||||
"Hermes, I know nothing about love, but you know even less than that. You're a motorrad." Kino said, laughing, "You never know what could happen. I could have ended up alone like that insane lady after a year."
|
||||
|
||||
"That seems like a weird thing to fear." Hermes said, but Kino just smiled.
|
||||
|
||||
"Maybe, but even so, there was only one thing he said that I really related to."
|
||||
|
||||
"What's that?" Hermes asked.
|
||||
|
||||
"Being alone is a relief." Kino said, smiling.
|
||||
|
||||
Hermes sighed. "How selfish."
|
||||
|
||||
"How rude." Kino said. But then she just laughed. "And yet, you're not wrong."
|
||||
|
||||
-Country of Embrace- end
|
||||
@@ -1,357 +0,0 @@
|
||||
-Kino's Journey- Country of Regret
|
||||
|
||||
"Kino, do you ever regret the decisions you made?" Hermes asked, as the traveler and motorrad rode to their next destination.
|
||||
|
||||
"Not particularly. I try not to dwell on the past much." Kino replied.
|
||||
|
||||
"But if you were given the chance, would you do things differently?" Hermes continued.
|
||||
|
||||
Kino thought for a moment. "Maybe. It depends on what I'd be changing."
|
||||
|
||||
"Well, obviously." Hermes said, "But you could end up a completely different person with only a minor change. Like if you hadn't met Master, or if you'd stayed in your hometown."
|
||||
|
||||
"Well, I wouldn't want to change anything that drastic or important." Kino explained. "I'm more thinking I'd change what I had for lunch yesterday."
|
||||
|
||||
"Kino, I'm being serious here." Hermes pleaded, but Kino just laughed.
|
||||
|
||||
"I know, Hermes. To tell the truth, I don't know. I like who I am now, but I think everyone thinks of something they would have done differently in hindsight." Kino answered.
|
||||
|
||||
In the horizon, the two started to see a glimpse of the next country, its borders slightly covering the morning sunrise, causing a beautiful ray of light to shine on Kino and Hermes. It was as if the country was welcoming them itself.
|
||||
|
||||
"Wow, beautiful." Kino said, in awe of the sight. Even Hermes seemed taken with the beauty, as he was oddly quiet.
|
||||
|
||||
Soon, the country's border was in full view, and Kino headed for the entrance. The area around the walls was empty, not a guard in sight. A welcome change of pace.
|
||||
|
||||
"Kino, what made you pick this country?" Hermes asked, curiously.
|
||||
|
||||
"Nothing in particular. No one would tell me anything about it, so I got curious." Kino replied, "And that sight certainly helped."
|
||||
|
||||
Finally, the two arrived at the entrance. Stopping in front of the guard booth, Kino noticed no one was there. Thinking it was strange, she looked around for something to signal she had arrived, but didn't see anything.
|
||||
|
||||
"Well, I guess we won't be going into this country." Hermes said, giving up.
|
||||
|
||||
"Hermes, I'm not one to give up right when things are getting interesting." Kino said, turning off the motorrad's engine and hopping off his back.
|
||||
|
||||
"But there's no one here. How do you expect to get in?"
|
||||
|
||||
Kino took a deep breath, then screamed as loud as she could: "Hello, I'd like to enter your country! Please open the gate!"
|
||||
|
||||
Hermes sighed. "Kino you look ridiculous. Come on, let's go somewhere else."
|
||||
|
||||
Kino shook her head, hopping back onto her partner and starting his engine. But then, the two noticed the ground begin to shake.
|
||||
|
||||
"Whoa, Kino, what's that?" Hermes asked.
|
||||
|
||||
Then, the gate of the country opened, with not a peep from any of the country's leaders. Kino laughed.
|
||||
|
||||
"Well, I guess we get to go in after all."
|
||||
|
||||
***
|
||||
|
||||
An abandoned wasteland of a city laid before Kino and Hermes as they passed the gate into the country. Not a peep could be heard, aside from the cries of the crows that watched the two travelers wander.
|
||||
|
||||
"Kino, this country doesn't seem to be quite as energetic as I thought. I wonder why no one wanted to talk about it." Hermes said.
|
||||
|
||||
"I don't think they would be so secretive about a destroyed wasteland. There has to be something here. Someone let us in, after all." Kino explained.
|
||||
|
||||
"That's a good point, but how could anyone live here? It's just ruins. Toppled buildings, rats and crows. It wouldn't be very sanitary to live here." Hermes said, as a rat crept up next to his wheels, sniffing him. "Frankly, I don't even want to be here right now."
|
||||
|
||||
"Just a bit more Hermes, I want to make sure." Kino said, starting Hermes back up and riding towards one of the few buildings that still stood. It even had lights on, though it was much smaller than the fallen skyscrapers that lined the ground.
|
||||
|
||||
Once they arrived at the entrance to the building, Kino realized there didn't appear to be a door. Not even that it was gone, it simply never existed in the first place.
|
||||
|
||||
Kino slowed Hermes to a stop and hopped off, going to the motorrad's rear and opening the pack that contained most of Kino's belongings.
|
||||
|
||||
"Kino, what are you looking for?" Hermes asked, curiously. Then he realized. "You don't mean to..."
|
||||
|
||||
"Found it!" Kino exclaimed, taking out a small grenade. "I didn't think Ti's little gift would ever come in handy, but here we are."
|
||||
|
||||
"Kino... Sometimes I wonder if you're alright in the head." Hermes sighed.
|
||||
|
||||
"What do you expect me to do, give up?" Kino said, pulling the pin and chucking the grenade towards the building and ducking behind Hermes. After a brief moment, it blew up, and Kino peeked out from behind her companion.
|
||||
|
||||
"I don't know if I appreciate you using me as a meat shield." Hermes whined.
|
||||
|
||||
"You're a motorrad Hermes, you have no meat." Kino walked towards the building, which now had a fully accessible entrance, custom made by Kino. "Well, now we can go inside."
|
||||
|
||||
Kino walked back, grabbing Hermes' handles and wheeling him into the building through the new entrance.
|
||||
|
||||
It was bright inside the building, almost blinding at first, but Kino's vision soon cleared. And what lay before the two travelers made them both gasp.
|
||||
|
||||
Hundreds of people, most seeming to be in their later years, lay before the two, lined up on bunk beds along both sides of an incredibly long room. Every single one of them seemed to be asleep.
|
||||
|
||||
"Kino... I have a bad feeling about this." Hermes said, his voice quivering.
|
||||
|
||||
"Yeah, we should go." Kino said, but as they turned around, Kino began to feel light headed. "Hermes... I don't feel so hot..."
|
||||
|
||||
"Kino, just get on my back, we have to get out of here!" Hermes yelled, but Kino fell to the ground.
|
||||
|
||||
All she could hear as her eyes closed was Hermes screaming her name.
|
||||
|
||||
***
|
||||
|
||||
When Kino opened her eyes, she was laying in a bed, staring at an unfamiliar ceiling. At least, that's what she thought at first, before she suddenly realized where this old, wood ceiling belonged.
|
||||
|
||||
"It can't be..." Kino thought, as she jumped out of her bed. She quickly made her way to her mirror, shocked at what she saw.
|
||||
|
||||
The girl in the mirror was a young child in a small, pink dress. Her hair went down to her waist and she looked no older than eight or nine. This appearance, as well as Kino's location, her childhood hometown, made her realize what had happened.
|
||||
|
||||
She was in the body of her younger self, and none of her Journey had been real. Either it was all a dream, or some crazy magic had sent her to the past. Thinking the latter illogical, she simply sighed, then laughed.
|
||||
|
||||
"Well, I guess it was fun while it lasted." Kino said to herself in the mirror. "I wonder how close I am to becoming an adult."
|
||||
|
||||
Kino went over to her nightstand, where a small display was set up. Small chocolates wrapped in foil sat inside numbered boxes, most of which were empty, save for the ones marked from one to three.
|
||||
|
||||
"Ah, so three more days, and then it'll be time. I'll finally be an adult!" Kino said, taking one of the chocolates out, unwrapping it and popping it into her mouth. The sweet candy filled her mouth, and she smiled with glee.
|
||||
|
||||
"Darling, breakfast is ready!" She heard her mother yell.
|
||||
|
||||
"Coming!" Kino exclaimed happily, humming to herself as she walked out of her room and down the stairs to the dining hall. Her parents ran an inn, and Kino hoped to someday own that inn for herself.
|
||||
|
||||
When she made it to the dining hall, Kino's mother greeted her happily, and sat her plate on the table in front of Kino as she sat down. It was a simple meal, homemade bacon and egg omelettes, but Kino just gobbled it down without hesitation. "An innkeeper certainly knows how to make the best food!" She said with glee.
|
||||
|
||||
"Well, look who's in a good mood. Did something good happen?" Kino's mom said, but then she said the name of a flower for some reason.
|
||||
|
||||
"Not particularly, but what about that kind of flower?" Kino asked, confused.
|
||||
|
||||
"What do you mean darling? That's your name, are you still half-asleep?"
|
||||
|
||||
"Oh, yeah. Sorry, I guess so." Kino said laughing. "Sorry, I had a dream and everyone called me Kino in it."
|
||||
|
||||
"Kino? That's a strange name." Her mother said, smiling. "But in just three days, you won't have to worry about strange dreams or anything anymore. You'll be a true adult!"
|
||||
|
||||
"I know, I can't wait!" The young girl said, finishing her breakfast and jumping out of her seat. In the week leading up to her operation to become an adult, she was allowed to do whatever she wanted. So she decided she would spend the day exploring her village. "I'm heading out!"she yelled, slipping on her shoes and flinging the door open. Her mom waved as it closed behind her.
|
||||
|
||||
The young girl skipped merrily to the town's beautiful fountain centerpiece, humming the entire way. Once she arrived, she hopped onto the side and sat happily, closing her eyes and taking in the spring breeze.
|
||||
|
||||
Her final day of relaxation was fast approaching, so she wanted to make the most of the time she had left. Adults didn't get breaks like this, after all, they had work to do.
|
||||
|
||||
"Excuse me." The young girl opened her eyes to see a young adult man looking down at her, smiling. "I'm a traveler, do you know where I can find an inn to stay at?"
|
||||
|
||||
"Sure, my family owns one, I can take you there." The young girl said, hopping off the fountain.
|
||||
|
||||
"Wow, thank you, that would be a big help!" The man said, sighing in relief. "I haven't been able to find anything, everyone seems so busy."
|
||||
|
||||
"Well, they are adults." The young girl said, smiling. "Just like I'll be in a few days!"
|
||||
|
||||
"Well, is that so? It's nice to meet, you, miss adult. By the way, I should properly introduce myself." The traveler said, bowing. "My name is Kino. I'll be staying here for the next three days."
|
||||
|
||||
***
|
||||
|
||||
The young girl watched through the window of her room as the traveler Kino sat outside, toying with the remnants of a small motorcycle. It mesmerized her, though she couldn't quite pinpoint why. It seemed to be a familiar sight, for some reason.
|
||||
|
||||
Suddenly Kino looked up towards the window, and the young girl jolted back, quickly shutting the curtains. She then folded herself in her covers, embarrassed to have been seen.
|
||||
|
||||
"Would you like to come see?" The young girl heard Kino call, causing her to jolt out of bed and peek out the curtains. Kino was now waving at her, smiling. The young girl quickly shut the curtains again, holding them shut and thinking to herself.
|
||||
|
||||
What if they wanted to hurt her? Take her captive and sell her? There were so many things that could happen if she went out to see the traveling outsider.
|
||||
|
||||
After all, the traveler never had the operation to become an adult.
|
||||
|
||||
But even so, curiosity got the best of her, and she slipped on her shoes and went towards the back of the inn where the traveler worked.
|
||||
|
||||
***
|
||||
|
||||
"Is that a motorcycle?" The young girl asked, curiously examining the beat up remains the traveler Kino was attempting to repair.
|
||||
|
||||
"Yes, it was given to me here so I could repair it, they said they had no use for it." The traveler explained. "Soon enough, it'll be good as new, chattering up a storm."
|
||||
|
||||
"You mean it can talk?" The young girl asked.
|
||||
|
||||
Kino just laughed. "Of course. This motorrad and I are going to be partners. A simple pact."
|
||||
|
||||
"Pact?"
|
||||
|
||||
"Yes. I give the motorrad balance, and he gives me speed. It benefits us both." The traveler explained. "Without me to balance it, the motorrad falls over, and I can go much faster if I ride him than if I walk."
|
||||
|
||||
"That does sound like it would be very helpful. Does he have a name?" The young girl asked.
|
||||
|
||||
Kino shook his head. "Not yet, why don't you help me come up with one?"
|
||||
|
||||
The young girl thought for a moment, but something quickly came to her mind. "Hermes." She said, almost on reflex.
|
||||
|
||||
"That sounds wonderful. That was also the name of my last partner." Kino said, smiling. "What made you think of that?"
|
||||
|
||||
"I'm not quite sure. He just brought that name to mind." The young girl said. She wasn't quite sure why herself.
|
||||
|
||||
"Maybe you can ride around with me once I get him repaired in a few days." Kino said, but the young girl shook her head.
|
||||
|
||||
"I won't be able to, I'm going to be an adult soon." The young girl explained. "In just three more days!"
|
||||
|
||||
"What makes you say that? You still look rather young to me." Kino asked, confused.
|
||||
|
||||
"Once we reach a certain age, we get our operation. They open our head and take the child from it. Then, we can do adult things like work with a smile, even if you hate it." The young girl explained.
|
||||
|
||||
Kino was very puzzled. "Do you think being able to do work you don't like is all it takes to be an adult?"
|
||||
|
||||
"That's what being an adult is, right? What job do you do, Kino?" The young girl asked.
|
||||
|
||||
"I'm a traveler." Kino said. "But I don't consider it work, I enjoy my journey."
|
||||
|
||||
"If you have fun, it isn't a job. It's just a hobby." The young girl said. "I don't think you're an adult yet, Kino."
|
||||
|
||||
"I suppose under your definition, that might be true." Kino said.
|
||||
|
||||
"What are you then? A child?" The young girl asked, confused.
|
||||
|
||||
Kino laughed. "I don't think that's quite right either."
|
||||
|
||||
"What are you then?"
|
||||
|
||||
Kino thought for a moment, then just smiled. "I'm just Kino."
|
||||
|
||||
The young girl laughed. "You don't make any sense, Kino."
|
||||
|
||||
"What about you, what do you want to do?" Kino asked.
|
||||
|
||||
"My parents run the inn, so once I become an adult I'll be helping them." The young girl said.
|
||||
|
||||
"No, not what job you'll have. What do you do for fun?" Kino rephrased.
|
||||
|
||||
Kino smiled brightly. "I like to sing!"
|
||||
|
||||
"How wonderful! I like to sing a lot on my travels myself. Though I'm not very good at it." Kino laughed, then sang a few verses of a song the young girl had never heard. His voice was shaky and his tone all over the place. He smiled afterwards. "Terrible, aren't I?"
|
||||
|
||||
"Horrible." The young girl said, laughing. Then, she breathed deeply inwards and sang the same lyrics, but with perfect pitch and tone.
|
||||
|
||||
Kino clapped. "Wonderful! You're the best singer I've come across on my entire journey!"
|
||||
|
||||
The young girl blushed.
|
||||
|
||||
"I'm serious. You should be a singer." Kino said, but the young girl shook her head.
|
||||
|
||||
"Kids are born to take over their parent's jobs. My parents aren't singers, so I can't be one."
|
||||
|
||||
"What a waste, then. You'd be a tremendous singer." Kino said, and the young girl just blushed again. Then she gasped, realizing the time.
|
||||
|
||||
"I've got to go back for dinner before my parents scold me." The young girl said, waving goodbye and hurrying away.
|
||||
|
||||
"I hope we meet again." Kino called out, as the young girl disappeared from his view.
|
||||
|
||||
***
|
||||
|
||||
The young girl stared at the countdown on her wall, which now only had one chocolate left. Today was the day she became an adult, but something inside her didn't feel quite as excited as before. Suddenly, it felt like there was more to life than just becoming an adult. The young girl wanted to do what she wanted in life, not what her parents chose for her.
|
||||
|
||||
She picked up the chocolate and pocketed it, before going down to the stairs to tell her parents what she'd decided to do.
|
||||
|
||||
Once you got downstairs, she saw her parents talking to some strange men she didn't recognize just outside the house.
|
||||
|
||||
"Congratulations, today, your daughter becomes a proper member of society. You have done a fine job raising such a respectable young lady." One of the men said, as the young girl's parents thanked them. "Oh, there's the star right now. Come here, young one." Another of the men said, gesturing the girl closer.
|
||||
|
||||
The young girl slowly made her way to the group, and took a deep breath. Something made her feel like saying she didn't want the operation anymore was a bad idea, but she couldn't quite pinpoint what it was.
|
||||
|
||||
"Dad, can I ask a question?" The girl said, turning to her smiling parents.
|
||||
|
||||
"What is it, sweetheart?" Her father asked.
|
||||
|
||||
The girl nervously shook. "What if I don't get the operation? What happens? Can I still be an adult?"
|
||||
|
||||
The girl and her parents stared at each other for a moment as the girl shook in terror. Her father and mother's loving smile stayed for a moment, but quickly turned to angry frowns.
|
||||
|
||||
Suddenly, her father exploded. "You idiot! How dare you question the wonderful operation! The thing keeping this country working!"
|
||||
|
||||
"That's right! And in front of this country's leaders! What a disgrace!" Her mother added.
|
||||
|
||||
Just then, the young girl saw Kino come out from behind the inn, rolling the motorrad he'd finished repairing along with him. Her parents saw her staring at the traveler, and they turned their hatred him.
|
||||
|
||||
"You! It was you who put these crazy, disgraceful ideas in my daughter's head! You evil man!" Her father cried.
|
||||
|
||||
Kino just smiled. "She decided for herself. But I won't deny I may have edged her on a bit."
|
||||
|
||||
The young girl ran towards Kino, crying. "Kino! I'm sorry, I got you in trouble."
|
||||
|
||||
"It's okay." Kino said, before looking towards the group that was staring them down.
|
||||
|
||||
"Traveler, I hope you are aware every country has their own customs and rules." One of the country's elders said to Kino, calmly.
|
||||
|
||||
"Yes, I am." Kino replied.
|
||||
|
||||
"Then you must also know it's not your job to get involved in the matters of these countries." The elder continued.
|
||||
|
||||
Kino smiled, "I was just thinking it was about time I left as well. I get the feeling something will happen to me if I stay any longer." He then turned to the young girl. "Really, I'll be fine. Go back to your parents."
|
||||
|
||||
"Okay..." The young girl said, and she walked back towards her mother. Her father had gone back inside.
|
||||
|
||||
"Do not worry, I will ensure your safety until you leave the walls of this country. That was part of the agreement we made when you entered." The elder said, smiling,
|
||||
|
||||
"Thank you, I appreciate that." Kino said, but his smile quickly turned to a frown when he spotted the young girl's father return, a smile on his face. "But what do you intend to do with that knife?"
|
||||
|
||||
The young girl trembled and froze, between Kino and the group of men. Something in her knew the next line the strange man would say.
|
||||
|
||||
"That? For disposing of this error, of course. Parents bring children into this world, and they have the right to remove ones that do not meet expectations." The elder said, as the young girl's father approached the young girl. She fell to her knees in terror. Her father began running, lifting the knife above his head.
|
||||
|
||||
Suddenly, the young girl saw the world turn crimson. But she felt no pain. She quickly realized why. Kino fell to the ground in front of her, the knife lodged in his chest. Her world froze. She reached her hand to her face, then pulled it back. It was covered in the same crimson that now formed a puddle around Kino.
|
||||
|
||||
Her father froze. "Oh, looks like this crazy man jumped in front of my knife! What a predicament!" He said, and the entire group broke into laughter. Her father went towards Kino, and tried to pull the knife from his chest. It was stuck tight. Everyone in the group gathered to help.
|
||||
|
||||
"As the man simply ran in front of the knife, there will be no charge. It's simply an unfortunate accident." The elder said, smiling.
|
||||
|
||||
The young girl watched as the townspeople she once loved and cherished now gathered around the corpse of an innocent traveler and were working furiously to remove the weapon so they could kill her. She collapsed against the mottorad Kino had finally fixed. Then, she remembered. The dream she had had before wasn't the dream. This was.
|
||||
|
||||
"No matter how many times you relive this past, it will never change." Hermes said.
|
||||
|
||||
The young girl laughed. "I'm the reason Kino died. If he hadn't jumped in to save me none of this would've happened. He could've lived a long life, and I cut it short."
|
||||
|
||||
"That's true." Hermes replied.
|
||||
|
||||
The young girl burst into tears and turned around to face the mottorad. The world around them had frozen. It was just her and Hermes, everyone else was stuck in time. "Then why? Why would he throw his life away for a girl he met only two days before? Why do I deserve to live instead of him? What makes me special?"
|
||||
|
||||
"Absolutely nothing." Hermes replied. "You probably even deserve to live less."
|
||||
|
||||
"Then why? Why won't the world let him live instead? Let me just die here." The young girl said, collapsing into tears.
|
||||
|
||||
"Would Kino want that?" Hermes said.
|
||||
|
||||
The young girl didn't answer. She just continued to cry.
|
||||
|
||||
"Kino chose this himself. He didn't need to save you. He could have walked away. But he did. And that was his own decision. You should honor that." Hermes said. "Live, Kino. Live."
|
||||
|
||||
The young girl turned around. There, standing before her, was Kino. "Kino..." She ran to him and jumped into his arms.
|
||||
|
||||
"I need you to live for me. The world needs to be seen in the eyes of a child. That's when it's at its most beautiful." Kino said, hugging the girl.
|
||||
|
||||
"But why do I deserve to see that and not you?" The young girl said.
|
||||
|
||||
Kino just laughed. "Everyone deserves to live. No one deserves it more than anyone else. I wasn't trading my life for yours when I saved you. I was giving you a chance to fulfill your dreams. It's up to you what that means."
|
||||
|
||||
The young girl just clung tighter to Kino.
|
||||
|
||||
"I would like to know your name though. I don't think I ever caught it." Kino asked.
|
||||
|
||||
The young girl let go of Kino and wiped the tears from her eyes, before finally cracking a smile. "My name, is Kino."
|
||||
|
||||
Reality returned before the young girl's eyes and she found herself once again overlooking the townspeople as they tried to remove the knife from Kino's body. The young girl quickly jumped onto the motorrad's seat and started it up, bolting away from the townspeople and making her way to the exit.
|
||||
|
||||
She passed by all the buildings she once loved. All the ones she once dreaded. Her entire childhood slowly faded into the mottorad's mirrors.
|
||||
|
||||
When she finally reached the exit, the guards were trying to quickly close the gate. But even as it started to fall, the young girl knew she'd make it. It was her destiny. She wasn't going to let the chance Kino had given to her go to waste.
|
||||
|
||||
And sure enough, she made it.
|
||||
|
||||
***
|
||||
|
||||
"Kino! Kino, please wake up! Kino!" Hermes called to his partner. Kino's eyes finally opened. "Finally! Thank goodness!"
|
||||
|
||||
"Hermes? What happened?" Kino said, feeling dizzy.
|
||||
|
||||
"Don't worry about that, get up quick, before you fall back asleep!" Hermes hurried Kino, who quickly got up and hopped onto the mottorad.
|
||||
|
||||
Kino hurriedly started up her partner and the two zoomed away, out of the building and then, the country's main gate. The moment the two passed through, it closed forcefully behind them.
|
||||
|
||||
The two rode onwards without a word as the country became smaller and smaller behind them. Hermes could tell Kino was deep in thought.
|
||||
|
||||
"You know, one of the robots of that country that watched over all those sleeping people told me what their country is all about." Hermes finally said. Kino didn't respond, but Hermes continued. "They send people into a state of deep sleep, where they get the chance to relive their most prominent regret. They can change that fate and then relive their entire life from that moment, without a single memory of who they were before. It's basically immortality."
|
||||
|
||||
"I see. What a terrible idea." Kino finally spoke, "It only makes it all worse."
|
||||
|
||||
"Too bad, it seemed like a good idea to me." Hermes replied.
|
||||
|
||||
"It just made it all more painful." Kino said, her voice wavering as she held more and more tightly to Hermes' handles.
|
||||
|
||||
"Would you say you regret going there, then?" Hermes asked.
|
||||
|
||||
Kino stopped Hermes and wiped her eyes, before finally smiling. "I will admit, it was nice to let it all out. Let's go."
|
||||
|
||||
Kino started Hermes back up, and they continued onwards to their next destination.
|
||||
|
||||
-Country of Regret- end
|
||||
@@ -1,63 +0,0 @@
|
||||
-Kino's Journey- The Honest Truth
|
||||
|
||||
The stars of the night shone over Kino and Hermes, but the trees of the forest shrouded them from view as they camped out by the fire. Kino was laying in thought, her eyes closed but her mind alert in case anyone tried to get the jump on them while they were vulnerable.
|
||||
|
||||
"Kino, are you still awake?" Hermes asked his partner.
|
||||
|
||||
"At the moment." Kino answered. "What's the matter?"
|
||||
|
||||
"Nothing really, I was just curious what was on your mind." Hermes said, a curious tone in his voice.
|
||||
|
||||
"Honestly, I don't think I really have much to say right now." Kino replied. "I'm just pondering about the world itself, really."
|
||||
|
||||
"Hmm, and what about the world?" Hermes asked.
|
||||
|
||||
"Well, let's see." Kino said, thinking for a moment. "How about this one: Why do people value honesty so much, but only for others?"
|
||||
|
||||
"What do you mean?" Hermes asked, confused.
|
||||
|
||||
"People are perfectly content to lie when it benefits them, but when they are lied to, they don't take it kindly. It's a bit hypocritical to me." Kino explained.
|
||||
|
||||
"What, are you saying you want people to lie to you?" Hermes asked.
|
||||
|
||||
"Well, no, I'm just saying that if people want honesty so badly, why are they so hesitant to provide it themselves?" Kino said.
|
||||
|
||||
"I feel like we've met enough selfish people on our journey for you to understand the answer to that. Humans are very selfish creatures. Frankly, you scare me." Hermes said.
|
||||
|
||||
"Sure, but we've also met selfless people as well, so that's not really a sound argument." Kino retorted.
|
||||
|
||||
"It's impossible for us to know if they were truly selfless or or simply after some ulterior motive they didn't tell us." Hermes explained.
|
||||
|
||||
Kino chuckled. "Assuming the worst in everyone is a very depressing worldview, Hermes."
|
||||
|
||||
"Then how should I look at it? Assuming the best will get us killed." Hermes said.
|
||||
|
||||
Kino just smiled. "Perhaps you're right." She said, before turning over to her side.
|
||||
|
||||
"Finally going to bed?" Hermes asked.
|
||||
|
||||
"Do you want the honest answer, or the one that makes you happier?" Kino asked, smiling.
|
||||
|
||||
"I don't think I'd be able to tell the difference between the honest answer and the lie, honestly." Hermes said.
|
||||
|
||||
"Well, that proves one part of my theory." Kino said.
|
||||
|
||||
"And what part is that?" Hermes asked.
|
||||
|
||||
"If they never find out the truth, a lie is just as honest as the truth." Kino said, chuckling.
|
||||
|
||||
"Well, that's just plain wrong." Hermes said, annoyed.
|
||||
|
||||
Kino laughed. "You said it yourself, Hermes. You'd never be able to tell."
|
||||
|
||||
"What a terrible person you are, Kino." Hermes said.
|
||||
|
||||
"Go to sleep, Hermes." Kino said, smiling. "We have to go to the next country tomorrow, and I don't want you sleeping in."
|
||||
|
||||
"Fine, good night." Hermes said. "Please don't stay up too long thinking about these things."
|
||||
|
||||
Kino laughed. "I won't."
|
||||
|
||||
And the traveler and her partner drifted off, their next adventure just beyond the horizon.
|
||||
|
||||
-Interlude "The Honest Truth" end-
|
||||
@@ -1,4 +0,0 @@
|
||||
TXT
|
||||
filter remove_matches .*
|
||||
extension txt
|
||||
3rd_gen_scale 1.00
|
||||
@@ -1,76 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const request = require('node-superfetch');
|
||||
const { oneLine } = require('common-tags');
|
||||
const { base64 } = require('../../util/Util');
|
||||
const { FACEPLUSPLUS_KEY, FACEPLUSPLUS_SECRET } = process.env;
|
||||
const emotions = ['anger', 'disgust', 'fear', 'happiness', 'neutral', 'sadness', 'surprise'];
|
||||
const emotionResponse = ['angry', 'disgusted', 'afraid', 'happy', 'uncaring', 'sad', 'surprised'];
|
||||
|
||||
module.exports = class FaceCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'face',
|
||||
group: 'analyze',
|
||||
memberName: 'face',
|
||||
description: 'Determines the race, gender, and age of a face.',
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 30
|
||||
},
|
||||
credit: [
|
||||
{
|
||||
name: 'Face++ Cognitive Services',
|
||||
url: 'https://www.faceplusplus.com/',
|
||||
reason: 'Face Detection API',
|
||||
reasonURL: 'https://www.faceplusplus.com/face-detection/'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'image',
|
||||
prompt: 'What face would you like to scan?',
|
||||
type: 'image-or-avatar'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { image }) {
|
||||
try {
|
||||
const face = await this.detect(image);
|
||||
if (!face) return msg.reply('There are no faces in this image.');
|
||||
if (face === 'size') return msg.reply('This image is too large.');
|
||||
const pronoun = face.gender.value === 'Male' ? 'He' : 'She';
|
||||
const emotion = emotionResponse[emotions.indexOf(
|
||||
emotions.slice(0).sort((a, b) => face.emotion[b] - face.emotion[a])[0]
|
||||
)];
|
||||
const smile = face.smile.value > face.smile.threshold;
|
||||
const beautyScore = face.gender.value === 'Male' ? face.beauty.female_score : face.beauty.male_score;
|
||||
return msg.reply(oneLine`
|
||||
I think this is a photo of a ${face.age.value} year old ${face.gender.value.toLowerCase()}.
|
||||
${pronoun} appears to be ${emotion}, and is ${smile ? 'smiling' : 'not smiling'}. I give this
|
||||
face a ${Math.round(beautyScore)} on the 1-100 beauty scale.
|
||||
${beautyScore > 50 ? beautyScore > 70 ? beautyScore > 90 ? 'Hot!' : 'Not bad.' : 'Not _too_ ugly.' : 'Uggggly!'}
|
||||
`);
|
||||
} catch (err) {
|
||||
if (err.status === 400) return msg.reply('There are no faces in this image.');
|
||||
if (err.status === 403) return msg.reply('Hold your horses! The command is overloaded! Try again soon.');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
|
||||
async detect(image) {
|
||||
const imgData = await request.get(image);
|
||||
if (Buffer.byteLength(imgData.body) >= 2e+6) return 'size';
|
||||
const { body } = await request
|
||||
.post('https://api-us.faceplusplus.com/facepp/v3/detect')
|
||||
.attach('image_base64', base64(imgData.body))
|
||||
.query({
|
||||
api_key: FACEPLUSPLUS_KEY,
|
||||
api_secret: FACEPLUSPLUS_SECRET,
|
||||
return_attributes: 'gender,age,smiling,emotion,ethnicity,beauty'
|
||||
});
|
||||
if (!body.faces || !body.faces.length) return null;
|
||||
return body.faces[0].attributes;
|
||||
}
|
||||
};
|
||||
@@ -7,8 +7,7 @@ module.exports = class CleverbotEndCommand extends Command {
|
||||
aliases: ['clevs-end', 'chat-end', 'end'],
|
||||
group: 'cleverbot',
|
||||
memberName: 'cleverbot-end',
|
||||
description: 'Ends the current Cleverbot chat.',
|
||||
patronOnly: true
|
||||
description: 'Ends the current Cleverbot chat.'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ module.exports = class CleverbotCommand extends Command {
|
||||
group: 'cleverbot',
|
||||
memberName: 'cleverbot',
|
||||
description: 'Starts a Cleverbot conversation.',
|
||||
patronOnly: true,
|
||||
credit: [
|
||||
{
|
||||
name: 'Cleverbot',
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const Docs = require('discord.js-docs');
|
||||
|
||||
module.exports = class DocstCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'docs',
|
||||
aliases: ['discord-js-docs', 'discord-js', 'djs', 'djs-docs'],
|
||||
group: 'code',
|
||||
memberName: 'docs',
|
||||
description: 'Searches the discord.js docs for your query.',
|
||||
clientPermissions: ['EMBED_LINKS'],
|
||||
args: [
|
||||
{
|
||||
key: 'query',
|
||||
prompt: 'What do you want to search the docs for?',
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { query }) {
|
||||
const doc = await Docs.fetch('stable');
|
||||
const embed = doc.resolveEmbed(query);
|
||||
if (!embed) return msg.say('Could not find any results.');
|
||||
return msg.embed(embed);
|
||||
}
|
||||
};
|
||||
@@ -1,94 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const request = require('node-superfetch');
|
||||
const { createCanvas, loadImage } = require('canvas');
|
||||
const path = require('path');
|
||||
const { base64 } = require('../../util/Util');
|
||||
const { FACEPLUSPLUS_KEY, FACEPLUSPLUS_SECRET } = process.env;
|
||||
|
||||
module.exports = class AnimeEyesCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'anime-eyes',
|
||||
aliases: ['ani-eyes', 'manga-eyes'],
|
||||
group: 'edit-face',
|
||||
memberName: 'anime-eyes',
|
||||
description: 'Draws anime eyes onto the faces in an image.',
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 60
|
||||
},
|
||||
credit: [
|
||||
{
|
||||
name: 'Face++ Cognitive Services',
|
||||
url: 'https://www.faceplusplus.com/',
|
||||
reason: 'Face Detection API',
|
||||
reasonURL: 'https://www.faceplusplus.com/face-detection/'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'image',
|
||||
prompt: 'What face would you like to scan?',
|
||||
type: 'image-or-avatar'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { image }) {
|
||||
const leftEye = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'anime-eyes', 'left.png'));
|
||||
const rightEye = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'anime-eyes', 'right.png'));
|
||||
const imgData = await request.get(image);
|
||||
try {
|
||||
const faces = await this.detect(imgData);
|
||||
if (!faces) return msg.reply('There are no faces in this image.');
|
||||
if (faces === 'size') return msg.reply('This image is too large.');
|
||||
const base = await loadImage(imgData.body);
|
||||
const canvas = createCanvas(base.width, base.height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(base, 0, 0);
|
||||
for (const face of faces) {
|
||||
const landmarks = face.landmark;
|
||||
const leftWidth = landmarks.left_eye_right_corner.x - landmarks.left_eye_left_corner.x;
|
||||
const leftRatio = leftWidth / leftEye.width;
|
||||
const leftHeight = leftEye.height * leftRatio;
|
||||
ctx.drawImage(
|
||||
leftEye,
|
||||
landmarks.left_eye_left_corner.x - (leftWidth * 0.25),
|
||||
landmarks.left_eye_left_corner.y - (leftHeight / 2) - (leftHeight * 0.25),
|
||||
leftWidth * 1.5,
|
||||
leftHeight * 1.5
|
||||
);
|
||||
const rightWidth = landmarks.right_eye_right_corner.x - landmarks.right_eye_left_corner.x;
|
||||
const rightRatio = rightWidth / rightEye.width;
|
||||
const rightHeight = rightEye.height * rightRatio;
|
||||
ctx.drawImage(
|
||||
rightEye,
|
||||
landmarks.right_eye_left_corner.x - (rightWidth * 0.25),
|
||||
landmarks.right_eye_left_corner.y - (rightHeight / 2) - (rightHeight * 0.25),
|
||||
rightWidth * 1.5,
|
||||
rightHeight * 1.5
|
||||
);
|
||||
}
|
||||
return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'anime-eyes.png' }] });
|
||||
} catch (err) {
|
||||
if (err.status === 400) return msg.reply('There are no faces in this image.');
|
||||
if (err.status === 403) return msg.reply('Hold your horses! The command is overloaded! Try again soon.');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
|
||||
async detect(imgData) {
|
||||
if (Buffer.byteLength(imgData.body) >= 2e+6) return 'size';
|
||||
const { body } = await request
|
||||
.post('https://api-us.faceplusplus.com/facepp/v3/detect')
|
||||
.attach('image_base64', base64(imgData.body))
|
||||
.query({
|
||||
api_key: FACEPLUSPLUS_KEY,
|
||||
api_secret: FACEPLUSPLUS_SECRET,
|
||||
return_landmark: 1
|
||||
});
|
||||
if (!body.faces || !body.faces.length) return null;
|
||||
return body.faces;
|
||||
}
|
||||
};
|
||||
@@ -1,88 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const request = require('node-superfetch');
|
||||
const { createCanvas, loadImage } = require('canvas');
|
||||
const path = require('path');
|
||||
const { base64 } = require('../../util/Util');
|
||||
const { FACEPLUSPLUS_KEY, FACEPLUSPLUS_SECRET } = process.env;
|
||||
|
||||
module.exports = class DannyDevitoCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'danny-devito',
|
||||
aliases: ['devito'],
|
||||
group: 'edit-face',
|
||||
memberName: 'danny-devito',
|
||||
description: 'Draws Danny Devito\'s face onto the faces in an image.',
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 60
|
||||
},
|
||||
credit: [
|
||||
{
|
||||
name: 'Danny DeVito',
|
||||
url: 'https://twitter.com/dannydevito',
|
||||
reason: 'Himself'
|
||||
},
|
||||
{
|
||||
name: 'Face++ Cognitive Services',
|
||||
url: 'https://www.faceplusplus.com/',
|
||||
reason: 'Face Detection API',
|
||||
reasonURL: 'https://www.faceplusplus.com/face-detection/'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'image',
|
||||
prompt: 'What face would you like to scan?',
|
||||
type: 'image-or-avatar'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { image }) {
|
||||
const danny = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'danny-devito.png'));
|
||||
const imgData = await request.get(image);
|
||||
try {
|
||||
const faces = await this.detect(imgData);
|
||||
if (!faces) return msg.reply('There are no faces in this image.');
|
||||
if (faces === 'size') return msg.reply('This image is too large.');
|
||||
const base = await loadImage(imgData.body);
|
||||
const canvas = createCanvas(base.width, base.height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(base, 0, 0);
|
||||
for (const face of faces) {
|
||||
const landmarks = face.landmark;
|
||||
const width = landmarks.contour_right1.x - landmarks.contour_left1.x;
|
||||
const ratio = width / danny.width;
|
||||
const height = danny.height * ratio;
|
||||
ctx.drawImage(
|
||||
danny,
|
||||
landmarks.contour_left1.x - (width * 0.25),
|
||||
landmarks.contour_left1.y - (height / 2) - (height * 0.25),
|
||||
width * 1.5,
|
||||
height * 1.5
|
||||
);
|
||||
}
|
||||
return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'danny-devito.png' }] });
|
||||
} catch (err) {
|
||||
if (err.status === 400) return msg.reply('There are no faces in this image.');
|
||||
if (err.status === 403) return msg.reply('Hold your horses! The command is overloaded! Try again soon.');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
|
||||
async detect(imgData) {
|
||||
if (Buffer.byteLength(imgData.body) >= 2e+6) return 'size';
|
||||
const { body } = await request
|
||||
.post('https://api-us.faceplusplus.com/facepp/v3/detect')
|
||||
.attach('image_base64', base64(imgData.body))
|
||||
.query({
|
||||
api_key: FACEPLUSPLUS_KEY,
|
||||
api_secret: FACEPLUSPLUS_SECRET,
|
||||
return_landmark: 1
|
||||
});
|
||||
if (!body.faces || !body.faces.length) return null;
|
||||
return body.faces;
|
||||
}
|
||||
};
|
||||
@@ -1,102 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const { GuildEmoji } = require('discord.js');
|
||||
const request = require('node-superfetch');
|
||||
const { createCanvas, loadImage } = require('canvas');
|
||||
const twemoji = require('twemoji-parser');
|
||||
const { base64 } = require('../../util/Util');
|
||||
const { FACEPLUSPLUS_KEY, FACEPLUSPLUS_SECRET } = process.env;
|
||||
|
||||
module.exports = class EmojiFaceCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'emoji-face',
|
||||
aliases: ['emoji-f', 'e-face'],
|
||||
group: 'edit-face',
|
||||
memberName: 'emoji-face',
|
||||
description: 'Draws an emoji onto the faces in an image.',
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 60
|
||||
},
|
||||
credit: [
|
||||
{
|
||||
name: 'Face++ Cognitive Services',
|
||||
url: 'https://www.faceplusplus.com/',
|
||||
reason: 'Face Detection API',
|
||||
reasonURL: 'https://www.faceplusplus.com/face-detection/'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'emoji',
|
||||
prompt: 'What emoji do you want to draw?',
|
||||
type: 'default-emoji|custom-emoji'
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
prompt: 'What face would you like to scan?',
|
||||
type: 'image-or-avatar'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { emoji, image }) {
|
||||
let emojiURL;
|
||||
if (emoji instanceof GuildEmoji) {
|
||||
emojiURL = emoji.url;
|
||||
} else {
|
||||
const parsed = twemoji.parse(emoji);
|
||||
if (!parsed.length || !parsed[0].url) return msg.reply('This emoji is not yet supported.');
|
||||
emojiURL = parsed[0].url;
|
||||
}
|
||||
const emojiData = await request.get(emojiURL);
|
||||
const emojiImg = await loadImage(emojiData.body);
|
||||
if (emojiURL.endsWith('svg')) {
|
||||
emojiImg.width *= 4;
|
||||
emojiImg.height *= 4;
|
||||
}
|
||||
const imgData = await request.get(image);
|
||||
try {
|
||||
const faces = await this.detect(imgData);
|
||||
if (!faces) return msg.reply('There are no faces in this image.');
|
||||
if (faces === 'size') return msg.reply('This image is too large.');
|
||||
const base = await loadImage(imgData.body);
|
||||
const canvas = createCanvas(base.width, base.height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(base, 0, 0);
|
||||
for (const face of faces) {
|
||||
const landmarks = face.landmark;
|
||||
const width = landmarks.contour_right1.x - landmarks.contour_left1.x;
|
||||
const ratio = width / emojiImg.width;
|
||||
const height = emojiImg.height * ratio;
|
||||
ctx.drawImage(
|
||||
emojiImg,
|
||||
landmarks.contour_left1.x - (width * 0.25),
|
||||
landmarks.contour_left1.y - (height / 2) - (height * 0.25),
|
||||
width * 1.5,
|
||||
height * 1.5
|
||||
);
|
||||
}
|
||||
return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'emoji-face.png' }] });
|
||||
} catch (err) {
|
||||
if (err.status === 400) return msg.reply('There are no faces in this image.');
|
||||
if (err.status === 403) return msg.reply('Hold your horses! The command is overloaded! Try again soon.');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
|
||||
async detect(imgData) {
|
||||
if (Buffer.byteLength(imgData.body) >= 2e+6) return 'size';
|
||||
const { body } = await request
|
||||
.post('https://api-us.faceplusplus.com/facepp/v3/detect')
|
||||
.attach('image_base64', base64(imgData.body))
|
||||
.query({
|
||||
api_key: FACEPLUSPLUS_KEY,
|
||||
api_secret: FACEPLUSPLUS_SECRET,
|
||||
return_landmark: 1
|
||||
});
|
||||
if (!body.faces || !body.faces.length) return null;
|
||||
return body.faces;
|
||||
}
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const request = require('node-superfetch');
|
||||
const { createCanvas, loadImage } = require('canvas');
|
||||
const path = require('path');
|
||||
const { base64 } = require('../../util/Util');
|
||||
const { FACEPLUSPLUS_KEY, FACEPLUSPLUS_SECRET } = process.env;
|
||||
|
||||
module.exports = class EyesCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'eyes',
|
||||
group: 'edit-face',
|
||||
memberName: 'eyes',
|
||||
description: 'Draws emoji eyes onto the faces in an image.',
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 60
|
||||
},
|
||||
credit: [
|
||||
{
|
||||
name: 'Face++ Cognitive Services',
|
||||
url: 'https://www.faceplusplus.com/',
|
||||
reason: 'Face Detection API',
|
||||
reasonURL: 'https://www.faceplusplus.com/face-detection/'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'image',
|
||||
prompt: 'What face would you like to scan?',
|
||||
type: 'image-or-avatar'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { image }) {
|
||||
const eyes = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'eyes.png'));
|
||||
const imgData = await request.get(image);
|
||||
try {
|
||||
const faces = await this.detect(imgData);
|
||||
if (!faces) return msg.reply('There are no faces in this image.');
|
||||
if (faces === 'size') return msg.reply('This image is too large.');
|
||||
const base = await loadImage(imgData.body);
|
||||
const canvas = createCanvas(base.width, base.height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(base, 0, 0);
|
||||
for (const face of faces) {
|
||||
const landmarks = face.landmark;
|
||||
const leftWidth = landmarks.left_eye_right_corner.x - landmarks.left_eye_left_corner.x;
|
||||
const leftRatio = leftWidth / eyes.width;
|
||||
const leftHeight = eyes.height * leftRatio;
|
||||
ctx.drawImage(
|
||||
eyes,
|
||||
landmarks.left_eye_left_corner.x - (leftWidth / 2),
|
||||
landmarks.left_eye_left_corner.y - (leftHeight / 2) - (leftHeight / 2),
|
||||
leftWidth * 2,
|
||||
leftHeight * 2
|
||||
);
|
||||
const rightWidth = landmarks.right_eye_right_corner.x - landmarks.right_eye_left_corner.x;
|
||||
const rightRatio = rightWidth / eyes.width;
|
||||
const rightHeight = eyes.height * rightRatio;
|
||||
ctx.drawImage(
|
||||
eyes,
|
||||
landmarks.right_eye_left_corner.x - (rightWidth / 2),
|
||||
landmarks.right_eye_left_corner.y - (rightHeight / 2) - (rightHeight / 2),
|
||||
rightWidth * 2,
|
||||
rightHeight * 2
|
||||
);
|
||||
}
|
||||
return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'eyes.png' }] });
|
||||
} catch (err) {
|
||||
if (err.status === 400) return msg.reply('There are no faces in this image.');
|
||||
if (err.status === 403) return msg.reply('Hold your horses! The command is overloaded! Try again soon.');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
|
||||
async detect(imgData) {
|
||||
if (Buffer.byteLength(imgData.body) >= 2e+6) return 'size';
|
||||
const { body } = await request
|
||||
.post('https://api-us.faceplusplus.com/facepp/v3/detect')
|
||||
.attach('image_base64', base64(imgData.body))
|
||||
.query({
|
||||
api_key: FACEPLUSPLUS_KEY,
|
||||
api_secret: FACEPLUSPLUS_SECRET,
|
||||
return_landmark: 1
|
||||
});
|
||||
if (!body.faces || !body.faces.length) return null;
|
||||
return body.faces;
|
||||
}
|
||||
};
|
||||
@@ -1,143 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const { MessageActionRow, MessageButton } = require('discord.js');
|
||||
const { stripIndents } = require('common-tags');
|
||||
const { Util: { escapeMarkdown } } = require('discord.js');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { readFile } = require('fs/promises');
|
||||
const stories = fs.readdirSync(path.join(__dirname, '..', '..', 'assets', 'txt', 'kino'))
|
||||
.map(story => story.slice(3).replace('.txt', ''));
|
||||
|
||||
module.exports = class KinoCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'kino',
|
||||
aliases: ['kino-journey', 'kinos-journey', 'kino\'s-journey'],
|
||||
group: 'other',
|
||||
memberName: 'kino',
|
||||
description: 'Read various Kino\'s Journey fan stories by dragonfire535.',
|
||||
details: stripIndents`
|
||||
**Stories:**
|
||||
\`\`\`
|
||||
${stories.map((story, i) => `${i.toString().padStart(2, '0')}. ${story}`).join('\n')}
|
||||
\`\`\`
|
||||
`,
|
||||
credit: [
|
||||
{
|
||||
name: 'Kino\'s Journey',
|
||||
url: 'https://kinonotabi.com/',
|
||||
reason: 'Original Concept'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'story',
|
||||
prompt: stripIndents`
|
||||
What story do you want to read? You can type the number or the name.
|
||||
\`\`\`
|
||||
${stories.map((story, i) => `${i.toString().padStart(2, '0')}. ${story}`).join('\n')}
|
||||
\`\`\`
|
||||
`,
|
||||
type: 'string',
|
||||
validate: choice => {
|
||||
if (stories.some(story => choice.toLowerCase() === story.toLowerCase())) return true;
|
||||
const num = Number.parseInt(choice, 10);
|
||||
return Boolean(stories[num]);
|
||||
},
|
||||
parse: choice => {
|
||||
if (stories.some(story => choice.toLowerCase() === story.toLowerCase())) {
|
||||
return choice.toLowerCase();
|
||||
}
|
||||
const num = Number.parseInt(choice, 10);
|
||||
return stories[num].toLowerCase();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { story }) {
|
||||
const storyData = await this.generateStory(story);
|
||||
let i = 0;
|
||||
let end = false;
|
||||
const row = new MessageActionRow();
|
||||
row.addComponents(
|
||||
new MessageButton().setCustomId('prev').setLabel('Prev').setStyle('PRIMARY').setDisabled(true),
|
||||
new MessageButton().setCustomId('next').setLabel('Next').setStyle('PRIMARY'),
|
||||
new MessageButton().setCustomId('end').setLabel('End').setStyle('DANGER')
|
||||
);
|
||||
const initialMsg = await msg.say(stripIndents`
|
||||
Welcome to Kino's Journey!
|
||||
Press the "Next" button to go to the next page, and "Prev" to go back.
|
||||
Press "End" at any time to stop reading.
|
||||
`, { components: [row] });
|
||||
const filter = res => res.user.id === msg.author.id;
|
||||
const initialInteractions = await initialMsg.awaitMessageComponent({
|
||||
filter,
|
||||
max: 1,
|
||||
time: 15000
|
||||
});
|
||||
if (!initialInteractions.size) return initialMsg.edit('Maybe next time!', { components: [] });
|
||||
let choice = initialInteractions.first();
|
||||
if (choice.customId === 'end') return choice.update('Maybe next time!', { components: [] });
|
||||
while (!end) {
|
||||
if (i === 0) {
|
||||
row.components[0].setDisabled(true);
|
||||
} else {
|
||||
row.components[0].setDisabled(false);
|
||||
}
|
||||
const line = storyData[i];
|
||||
if (!line) {
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
await choice.update(stripIndents`
|
||||
**Page ${i + 1}/${storyData.length}**
|
||||
|
||||
${escapeMarkdown(line.trim())}
|
||||
`, { components: [row] });
|
||||
const interactions = await initialMsg.awaitMessageComponent({
|
||||
filter,
|
||||
max: 1,
|
||||
time: 120000
|
||||
});
|
||||
if (!interactions.size) break;
|
||||
choice = interactions.first();
|
||||
if (choice.customId === 'next') {
|
||||
i++;
|
||||
} else if (choice.customId === 'prev') {
|
||||
i--;
|
||||
} else if (choice.customId === 'end') {
|
||||
break;
|
||||
} else {
|
||||
return initialMsg.edit('Maybe next time!', { components: [] });
|
||||
}
|
||||
}
|
||||
return choice.update('Thank you for reading this chapter of Kino\'s Journey!', { components: [] });
|
||||
}
|
||||
|
||||
async generateStory(file) {
|
||||
const filename = stories.find(story => story.toLowerCase() === file);
|
||||
const num = stories.indexOf(filename).toString().padStart(2, '0');
|
||||
const story = await readFile(path.join(__dirname, '..', '..', 'assets', 'txt', 'kino', `${num} ${filename}.txt`), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
const chunks = [];
|
||||
let currentChunk = '';
|
||||
for (const paragraph of story.split('\n\n')) {
|
||||
if (paragraph === '***' && currentChunk) {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = '***\n\n';
|
||||
continue;
|
||||
}
|
||||
currentChunk += paragraph;
|
||||
currentChunk += '\n\n';
|
||||
if (currentChunk.length > 1000) {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = '';
|
||||
}
|
||||
}
|
||||
chunks.push(currentChunk);
|
||||
return chunks;
|
||||
}
|
||||
};
|
||||
@@ -40,9 +40,6 @@ module.exports = class ClocCommand extends Command {
|
||||
[
|
||||
'--json',
|
||||
'--exclude-dir=node_modules',
|
||||
'--read-lang-def',
|
||||
path.join(__dirname, '..', '..', 'assets', 'txt', 'txt_definition.txt'),
|
||||
'--force-lang=TXT,txt',
|
||||
path.join(__dirname, '..', '..')
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const { stripIndents } = require('common-tags');
|
||||
|
||||
module.exports = class DonateCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'donate',
|
||||
aliases: ['paypal', 'patreon'],
|
||||
group: 'util-public',
|
||||
memberName: 'donate',
|
||||
description: 'Responds with the bot\'s donation links.',
|
||||
guarded: true,
|
||||
credit: [
|
||||
{
|
||||
name: 'PayPal',
|
||||
url: 'https://www.paypal.com/us/home',
|
||||
reason: 'Donation Gathering'
|
||||
},
|
||||
{
|
||||
name: 'Patreon',
|
||||
url: 'https://www.patreon.com/',
|
||||
reason: 'Donation Gathering'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
run(msg) {
|
||||
if (this.client.patreon.patrons.includes(msg.author.id)) {
|
||||
return msg.say('🎉 You are already a patron. Enjoy your rewards!');
|
||||
}
|
||||
return msg.say(stripIndents`
|
||||
Contribute to development!
|
||||
<https://www.patreon.com/xiaodiscord>
|
||||
<https://paypal.me/dragonfire535>
|
||||
`);
|
||||
}
|
||||
};
|
||||
@@ -11,7 +11,7 @@ module.exports = class InfoCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'info',
|
||||
aliases: ['stats', 'uptime'],
|
||||
aliases: ['stats', 'uptime', 'prefix', 'invite'],
|
||||
group: 'util-public',
|
||||
memberName: 'info',
|
||||
description: 'Responds with detailed bot information.',
|
||||
@@ -22,6 +22,7 @@ module.exports = class InfoCommand extends Command {
|
||||
|
||||
run(msg) {
|
||||
const invite = this.client.generateInvite({ permissions: ['ADMINISTRATOR'], scopes: ['bot'] });
|
||||
const prefix = msg.guild ? msg.guild.commandPrefix : this.client.commandPrefix;
|
||||
const embed = new MessageEmbed()
|
||||
.setColor(0x00AE86)
|
||||
.setFooter(copyright.join('\n'))
|
||||
@@ -31,7 +32,7 @@ module.exports = class InfoCommand extends Command {
|
||||
.addField('❯ Home Server',
|
||||
this.client.options.invite ? embedURL('Invite', this.client.options.invite) : 'None', true)
|
||||
.addField('❯ Invite', embedURL('Add Me', invite), true)
|
||||
.addField('❯ Donate', embedURL('Patreon', 'https://www.patreon.com/xiaodiscord'), true)
|
||||
.addField('❯ Prefix', prefix || 'None', true)
|
||||
.addField('❯ Memory Usage', `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`, true)
|
||||
.addField('❯ Uptime', moment.duration(this.client.uptime).format('d:hh:mm:ss'), true)
|
||||
.addField('❯ Version', `v${version}`, true)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const { stripIndents } = require('common-tags');
|
||||
const permissions = require('../../assets/json/permissions');
|
||||
|
||||
module.exports = class InviteCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'invite',
|
||||
group: 'util-public',
|
||||
memberName: 'invite',
|
||||
description: 'Responds with the bot\'s invite links.',
|
||||
guarded: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg) {
|
||||
const invite = await this.client.generateInvite({ permissions });
|
||||
return msg.say(stripIndents`
|
||||
Invite me using this link:
|
||||
<${invite}>
|
||||
|
||||
Join my home server for support and announcements:
|
||||
${this.client.options.invite || 'Coming soon...'}
|
||||
`);
|
||||
}
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
|
||||
module.exports = class PrefixCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'prefix',
|
||||
group: 'util-public',
|
||||
memberName: 'prefix',
|
||||
description: 'Responds with the bot\'s command prefix.',
|
||||
guarded: true
|
||||
});
|
||||
}
|
||||
|
||||
run(msg) {
|
||||
const prefix = msg.guild ? msg.guild.commandPrefix : this.client.commandPrefix;
|
||||
return msg.reply(prefix ? `The command prefix is \`${prefix}\`.` : 'There is no command prefix.');
|
||||
}
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
|
||||
module.exports = class ForcePatronCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'force-patron',
|
||||
group: 'util',
|
||||
memberName: 'force-patron',
|
||||
description: 'Allows a user to use patron-only commands.',
|
||||
details: 'Only the bot owner(s) may use this command.',
|
||||
ownerOnly: true,
|
||||
guarded: true,
|
||||
args: [
|
||||
{
|
||||
key: 'target',
|
||||
prompt: 'Who do you want to allow? Use the ID.',
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
run(msg, { target }) {
|
||||
if (this.client.patreon.isPatron(target)) return msg.say(`💸 \`${target}\` is already a patron.`);
|
||||
this.client.patreon.forced.push(target);
|
||||
this.client.patreon.exportForced();
|
||||
return msg.say(`💸 Allowed \`${target}\` to use patron-only commands.`);
|
||||
}
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const { removeFromArray } = require('../../util/Util');
|
||||
|
||||
module.exports = class UnforcePatronCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'unforce-patron',
|
||||
group: 'util',
|
||||
memberName: 'unforce-patron',
|
||||
description: 'Disallows a user from using patron-only commands.',
|
||||
details: 'Only the bot owner(s) may use this command.',
|
||||
ownerOnly: true,
|
||||
guarded: true,
|
||||
args: [
|
||||
{
|
||||
key: 'target',
|
||||
prompt: 'Who do you want to disallow? Use the ID.',
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
run(msg, { target }) {
|
||||
if (!this.client.patreon.isPatron(target)) return msg.say(`💸 \`${target}\` is not a patron.`);
|
||||
removeFromArray(this.client.patreon.forced, target);
|
||||
this.client.patreon.exportForced();
|
||||
return msg.say(`💸 Disallowed \`${target}\` from using patron-only commands.`);
|
||||
}
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
|
||||
module.exports = class WebhookCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'webhook',
|
||||
aliases: ['rin', 'rin-say'],
|
||||
group: 'util',
|
||||
memberName: 'webhook',
|
||||
description: 'Posts a message to the webhook defined in the bot owner\'s `process.env`.',
|
||||
details: 'Only the bot owner(s) may use this command.',
|
||||
ownerOnly: true,
|
||||
clientPermissions: ['MANAGE_MESSAGES'],
|
||||
args: [
|
||||
{
|
||||
key: 'content',
|
||||
prompt: 'What text would you like the webhook to say?',
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { content }) {
|
||||
try {
|
||||
if (msg.guild && msg.deletable) await msg.delete();
|
||||
await this.client.webhook.send(content);
|
||||
return null;
|
||||
} catch (err) {
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,141 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const path = require('path');
|
||||
const { list, reactIfAble } = require('../../util/Util');
|
||||
const sounds = require('../../assets/json/soundboard');
|
||||
const soundsChoice = sounds.map(sound => sound[sound.length - 1].replace(/\.mp3$/, ''));
|
||||
|
||||
module.exports = class SoundboardCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'soundboard',
|
||||
aliases: ['sound'],
|
||||
group: 'voice',
|
||||
memberName: 'soundboard',
|
||||
description: 'Plays a sound in a voice channel.',
|
||||
details: `**Sounds:** ${soundsChoice.join(', ')}`,
|
||||
guildOnly: true,
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 10
|
||||
},
|
||||
userPermissions: ['CONNECT', 'SPEAK'],
|
||||
credit: [
|
||||
{
|
||||
name: '07th Expansion',
|
||||
url: 'http://07th-expansion.net/',
|
||||
reason: 'Nipah Sound'
|
||||
},
|
||||
{
|
||||
name: 'UncleKornicob',
|
||||
url: 'http://soundbible.com/',
|
||||
reason: 'Alarm Sound',
|
||||
reasonURL: 'http://soundbible.com/1787-Annoying-Alarm-Clock.html'
|
||||
},
|
||||
{
|
||||
name: 'Mike Koenig',
|
||||
url: 'http://soundbible.com/',
|
||||
reason: 'Rooster Sound',
|
||||
reasonURL: 'http://soundbible.com/1218-Rooster-Crow.html'
|
||||
},
|
||||
{
|
||||
name: 'Mike Koenig',
|
||||
url: 'http://soundbible.com/',
|
||||
reason: 'Cow Sound',
|
||||
reasonURL: 'http://soundbible.com/1778-Cow-Moo.html'
|
||||
},
|
||||
{
|
||||
name: 'Cam Martinez',
|
||||
url: 'http://soundbible.com/',
|
||||
reason: 'Car Crash Sound',
|
||||
reasonURL: 'http://soundbible.com/1757-Car-Brake-Crash.html'
|
||||
},
|
||||
{
|
||||
name: 'Orange Free Sounds',
|
||||
url: 'http://www.orangefreesounds.com/',
|
||||
reason: 'Dun Dun Dun Sound',
|
||||
reasonURL: 'http://www.orangefreesounds.com/dun-dun-dun-sound-effect-brass/'
|
||||
},
|
||||
{
|
||||
name: 'Apple',
|
||||
url: 'https://www.apple.com/',
|
||||
reason: 'Cat Sound'
|
||||
},
|
||||
{
|
||||
name: 'GRSites',
|
||||
url: 'http://www.grsites.com/',
|
||||
reason: 'Laugh Track Sound',
|
||||
reasonURL: 'http://www.grsites.com/archive/sounds/category/8/'
|
||||
},
|
||||
{
|
||||
name: 'Jeopardy',
|
||||
url: 'https://www.jeopardy.com/',
|
||||
reason: 'Jeopardy Sound'
|
||||
},
|
||||
{
|
||||
name: '4Kids',
|
||||
url: 'https://www.4kidsentertainmentinc.com/',
|
||||
reason: 'Who\'s That Pokémon Sound'
|
||||
},
|
||||
{
|
||||
name: 'Over the Green Fields',
|
||||
url: 'https://asianwiki.com/Over_the_Green_Fields',
|
||||
reason: 'Sad Violin Sound'
|
||||
},
|
||||
{
|
||||
name: 'Valve',
|
||||
url: 'https://www.valvesoftware.com/en/',
|
||||
reasonURL: 'http://www.thinkwithportals.com/',
|
||||
reason: 'Slow Clap Sound'
|
||||
},
|
||||
{
|
||||
name: 'Microsoft',
|
||||
url: 'https://www.microsoft.com/en-us',
|
||||
reason: 'Windows Start Up and Windows Error Sounds'
|
||||
},
|
||||
{
|
||||
name: 'Star Wars',
|
||||
url: 'https://www.starwars.com/',
|
||||
reason: 'Hello There Sound'
|
||||
},
|
||||
{
|
||||
name: 'Rockstar Games',
|
||||
url: 'https://www.rockstargames.com/',
|
||||
reason: 'Here We Go Again Sound'
|
||||
},
|
||||
{
|
||||
name: 'KONOSUBA -God\'s blessing on this wonderful world!',
|
||||
url: 'http://konosuba.com/',
|
||||
reason: 'Explosion Sound'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'sound',
|
||||
prompt: `What sound do you want to play? Either ${list(soundsChoice, 'or')}.`,
|
||||
type: 'string',
|
||||
validate: sound => {
|
||||
const choice = sound.toLowerCase().replaceAll(' ', '-');
|
||||
if (soundsChoice.includes(choice)) return true;
|
||||
return `You provided an invalid sound. Please choose either ${list(soundsChoice, 'or')}.`;
|
||||
},
|
||||
parse: sound => {
|
||||
const choice = sound.toLowerCase().replaceAll(' ', '-');
|
||||
return sounds.find(snd => snd.includes(`${choice}.mp3`));
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { sound }) {
|
||||
const connection = this.client.dispatchers.get(msg.guild.id);
|
||||
if (!connection) {
|
||||
const usage = this.client.registry.commands.get('join').usage();
|
||||
return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`);
|
||||
}
|
||||
if (!connection.canPlay) return msg.reply('I am already playing audio in this server.');
|
||||
connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', ...sound));
|
||||
await reactIfAble(msg, this.client.user, '🔉');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -1,71 +0,0 @@
|
||||
const Command = require('../../framework/Command');
|
||||
const request = require('node-superfetch');
|
||||
const { Readable } = require('stream');
|
||||
const { list, reactIfAble } = require('../../util/Util');
|
||||
const voices = require('../../assets/json/vocodes');
|
||||
const { LOADING_EMOJI_ID } = process.env;
|
||||
|
||||
module.exports = class VocodesCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'vocodes',
|
||||
aliases: ['vocode'],
|
||||
group: 'voice',
|
||||
memberName: 'vocodes',
|
||||
description: 'Speak text like a variety of famous figures.',
|
||||
details: `**Voices:** ${Object.keys(voices).join(', ')}`,
|
||||
guildOnly: true,
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 30
|
||||
},
|
||||
userPermissions: ['CONNECT', 'SPEAK'],
|
||||
credit: [
|
||||
{
|
||||
name: 'Vocodes',
|
||||
url: 'https://vo.codes/',
|
||||
reason: 'API'
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
key: 'voice',
|
||||
prompt: `What voice do you want to use? Either ${list(Object.keys(voices), 'or')}.`,
|
||||
type: 'string',
|
||||
oneOf: Object.keys(voices),
|
||||
parse: voice => voices[voice.toLowerCase()]
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
prompt: 'What text do you want to say?',
|
||||
type: 'string',
|
||||
max: 500
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { voice, text }) {
|
||||
const connection = this.client.dispatchers.get(msg.guild.id);
|
||||
if (!connection) {
|
||||
const usage = this.client.registry.commands.get('join').usage();
|
||||
return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`);
|
||||
}
|
||||
if (!connection.canPlay) return msg.reply('I am already playing audio in this server.');
|
||||
try {
|
||||
await reactIfAble(msg, this.client.user, LOADING_EMOJI_ID, '💬');
|
||||
const { body } = await request
|
||||
.post('https://mumble.stream/speak_spectrogram')
|
||||
.send({
|
||||
speaker: voice,
|
||||
text
|
||||
});
|
||||
connection.play(Readable.from([Buffer.from(body.audio_base64, 'base64')]));
|
||||
await reactIfAble(msg, this.client.user, '🔉');
|
||||
return null;
|
||||
} catch (err) {
|
||||
await reactIfAble(msg, this.client.user, '⚠️');
|
||||
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -4,7 +4,6 @@ const path = require('path');
|
||||
const { stripIndents } = require('common-tags');
|
||||
const Registry = require('./Registry');
|
||||
const Dispatcher = require('./Dispatcher');
|
||||
const Patreon = require('../structures/Patreon');
|
||||
require('./Extensions');
|
||||
|
||||
module.exports = class CommandClient extends Client {
|
||||
@@ -17,7 +16,6 @@ module.exports = class CommandClient extends Client {
|
||||
this.invite = options.invite || null;
|
||||
this.registry = new Registry(this);
|
||||
this.dispatcher = new Dispatcher(this);
|
||||
this.patreon = new Patreon();
|
||||
this.blacklist = { user: [], guild: [] };
|
||||
this._throttlingTimeouts = new Map();
|
||||
|
||||
@@ -85,13 +83,6 @@ module.exports = class CommandClient extends Client {
|
||||
await msg.reply(`The \`${command.name}\` command can only be used in NSFW channels.`);
|
||||
return;
|
||||
}
|
||||
if (command.patronOnly && !this.patreon.isPatron(msg.author.id)) {
|
||||
await msg.reply(stripIndents`
|
||||
The \`${command.name}\` command can only be used by Patrons.
|
||||
Visit <https://www.patreon.com/xiaodiscord> to sign-up!
|
||||
`);
|
||||
return;
|
||||
}
|
||||
if (msg.guild && command.userPermissions.length) {
|
||||
for (const permission of command.userPermissions) {
|
||||
if (msg.channel.permissionsFor(msg.author).has(permission)) continue;
|
||||
|
||||
@@ -17,7 +17,6 @@ module.exports = class Command {
|
||||
this.ownerOnly = options.ownerOnly || false;
|
||||
this.nsfw = options.nsfw || false;
|
||||
this.guildOnly = options.guildOnly || false;
|
||||
this.patronOnly = options.patronOnly || false;
|
||||
this.guarded = options.guarded || false;
|
||||
this.unknown = options.unknown || false;
|
||||
this.throttling = options.throttling || { usages: 2, duration: 5 };
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"custom-translate": "^2.2.8",
|
||||
"didyoumean2": "^5.0.0",
|
||||
"discord.js": "^13.17.1",
|
||||
"discord.js-docs": "github:TeeSeal/discord.js-docs",
|
||||
"dotenv": "^16.4.5",
|
||||
"emoji-regex": "^10.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
const request = require('node-superfetch');
|
||||
const {
|
||||
TOP_GG_TOKEN,
|
||||
BOTS_GG_TOKEN,
|
||||
DISCORDBOTLIST_TOKEN,
|
||||
CARBON_TOKEN,
|
||||
BLIST_TOKEN
|
||||
} = process.env;
|
||||
|
||||
module.exports = class BotList {
|
||||
constructor(client) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.topGGToken = TOP_GG_TOKEN;
|
||||
this.botsGGToken = BOTS_GG_TOKEN;
|
||||
this.discordBotsListToken = DISCORDBOTLIST_TOKEN;
|
||||
this.carbonToken = CARBON_TOKEN;
|
||||
this.blistToken = BLIST_TOKEN;
|
||||
}
|
||||
|
||||
async postTopGGStats() {
|
||||
if (!this.topGGToken) return null;
|
||||
try {
|
||||
const { body } = await request
|
||||
.post(`https://top.gg/api/bots/${this.client.user.id}/stats`)
|
||||
.set({ Authorization: this.topGGToken })
|
||||
.send({ server_count: this.client.guilds.cache.size });
|
||||
this.client.logger.info('[TOP.GG] Posted stats.');
|
||||
return body;
|
||||
} catch (err) {
|
||||
this.client.logger.error(`[TOP.GG] Failed to post stats:\n${err.stack}`);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
async postBotsGGStats() {
|
||||
if (!this.botsGGToken) return null;
|
||||
try {
|
||||
const { body } = await request
|
||||
.post(`https://discord.bots.gg/api/v1/bots/${this.client.user.id}/stats`)
|
||||
.set({ Authorization: this.botsGGToken })
|
||||
.send({ guildCount: this.client.guilds.cache.size });
|
||||
this.client.logger.info('[BOTS.GG] Posted stats.');
|
||||
return body;
|
||||
} catch (err) {
|
||||
this.client.logger.error(`[BOTS.GG] Failed to post stats:\n${err.stack}`);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
async postDiscordBotListStats() {
|
||||
if (!this.discordBotsListToken) return null;
|
||||
try {
|
||||
const { body } = await request
|
||||
.post(`https://discordbotlist.com/api/v1/bots/${this.client.user.id}/stats`)
|
||||
.set({ Authorization: this.discordBotsListToken })
|
||||
.send({
|
||||
guilds: this.client.guilds.cache.size,
|
||||
users: this.client.users.cache.size,
|
||||
voice_connections: this.client.dispatchers.size
|
||||
});
|
||||
this.client.logger.info('[DISCORDBOTLIST] Posted stats.');
|
||||
return body;
|
||||
} catch (err) {
|
||||
this.client.logger.error(`[DISCORDBOTLIST] Failed to post stats:\n${err.stack}`);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
async postCarbonStats() {
|
||||
if (!this.carbonToken) return null;
|
||||
try {
|
||||
const { body } = await request
|
||||
.post('https://www.carbonitex.net/discord/data/botdata.php')
|
||||
.send({
|
||||
key: this.carbonToken,
|
||||
servercount: this.client.guilds.cache.size,
|
||||
botid: this.client.user.id
|
||||
});
|
||||
this.client.logger.info('[CARBON] Posted stats.');
|
||||
return body;
|
||||
} catch (err) {
|
||||
this.client.logger.error(`[CARBON] Failed to post stats:\n${err.stack}`);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
async postBlistStats() {
|
||||
if (!this.blistToken) return null;
|
||||
try {
|
||||
const { body } = await request
|
||||
.patch(`https://blist.xyz/api/v2/bot/${this.client.user.id}/stats/`)
|
||||
.set({ Authorization: this.blistToken })
|
||||
.send({ server_count: this.client.guilds.cache.size });
|
||||
this.client.logger.info('[BLIST] Posted stats.');
|
||||
return body;
|
||||
} catch (err) {
|
||||
this.client.logger.error(`[BLIST] Failed to post stats:\n${err.stack}`);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
+1
-14
@@ -1,5 +1,4 @@
|
||||
const CommandClient = require('../framework/Client');
|
||||
const { WebhookClient } = require('discord.js');
|
||||
const request = require('node-superfetch');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const winston = require('winston');
|
||||
@@ -11,19 +10,12 @@ const url = require('url');
|
||||
const path = require('path');
|
||||
const Redis = require('./Redis');
|
||||
const Font = require('./Font');
|
||||
const BotList = require('./BotList');
|
||||
const PhoneManager = require('./phone/PhoneManager');
|
||||
const TimerManager = require('./remind/TimerManager');
|
||||
const PokemonStore = require('./pokemon/PokemonStore');
|
||||
const activities = require('../assets/json/activity');
|
||||
const leaveMsgs = require('../assets/json/leave-messages');
|
||||
const {
|
||||
XIAO_WEBHOOK_ID,
|
||||
XIAO_WEBHOOK_TOKEN,
|
||||
REPORT_CHANNEL_ID,
|
||||
JOIN_LEAVE_CHANNEL_ID,
|
||||
COMMAND_CHANNEL_ID
|
||||
} = process.env;
|
||||
const { REPORT_CHANNEL_ID, JOIN_LEAVE_CHANNEL_ID, COMMAND_CHANNEL_ID } = process.env;
|
||||
|
||||
module.exports = class XiaoClient extends CommandClient {
|
||||
constructor(options) {
|
||||
@@ -38,12 +30,7 @@ module.exports = class XiaoClient extends CommandClient {
|
||||
});
|
||||
this.fonts = new Collection();
|
||||
this.redis = Redis ? Redis.db : null;
|
||||
this.webhook = new WebhookClient(
|
||||
{ id: XIAO_WEBHOOK_ID, token: XIAO_WEBHOOK_TOKEN },
|
||||
{ disableMentions: 'everyone' }
|
||||
);
|
||||
this.timers = new TimerManager(this);
|
||||
this.botList = new BotList(this);
|
||||
this.pokemon = new PokemonStore();
|
||||
this.games = new Collection();
|
||||
this.dispatchers = new Map();
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
const request = require('node-superfetch');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { PATREON_ACCESS_TOKEN, PATREON_CAMPAIGN_ID } = process.env;
|
||||
|
||||
module.exports = class Patreon {
|
||||
constructor(accessToken, campaignID) {
|
||||
this.patrons = [];
|
||||
this.forced = [];
|
||||
this.accessToken = accessToken || PATREON_ACCESS_TOKEN;
|
||||
this.campaignID = campaignID || PATREON_CAMPAIGN_ID;
|
||||
}
|
||||
|
||||
isPatron(id) {
|
||||
return this.patrons.includes(id) || this.forced.includes(id);
|
||||
}
|
||||
|
||||
async fetchPatrons() {
|
||||
if (!this.accessToken || !this.campaignID) return null;
|
||||
const { text } = await request
|
||||
.get(`https://www.patreon.com/api/oauth2/v2/campaigns/${PATREON_CAMPAIGN_ID}/members`)
|
||||
.set({ Authorization: `Bearer ${PATREON_ACCESS_TOKEN}` })
|
||||
.query({
|
||||
include: 'currently_entitled_tiers,user',
|
||||
'fields[user]': 'social_connections',
|
||||
'fields[member]': 'patron_status'
|
||||
});
|
||||
const body = JSON.parse(text);
|
||||
const patrons = [];
|
||||
for (const patron of body.data) {
|
||||
if (patron.attributes.patron_status !== 'active_patron') continue;
|
||||
const socials = body.included.find(user => user.id === patron.relationships.user.data.id)
|
||||
?.attributes?.social_connections;
|
||||
if (!socials || !socials.discord || !socials.discord.user_id) continue;
|
||||
patrons.push(socials.discord.user_id);
|
||||
}
|
||||
this.patrons = patrons;
|
||||
return this.patrons;
|
||||
}
|
||||
|
||||
importForced() {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'patreon.json'), { encoding: 'utf8' });
|
||||
const file = JSON.parse(read);
|
||||
if (!Array.isArray(file)) return null;
|
||||
for (const id of file) {
|
||||
if (typeof id !== 'string') continue;
|
||||
if (this.forced.includes(id)) continue;
|
||||
this.forced.push(id);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportForced() {
|
||||
let text = '[\n ';
|
||||
if (this.forced.length) {
|
||||
for (const id of this.forced) {
|
||||
text += `"${id}",\n `;
|
||||
}
|
||||
text = text.slice(0, -3);
|
||||
}
|
||||
text += '\n]\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'patreon.json'), buf, { encoding: 'utf8' });
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user