!7 concept CLotteryResult { S date; L numbers; S jackpot; [stdToString] } standardBot1 LotteryBot { long resultsChannelID; init { dbIndexing(CLotteryResult, 'date); onDiscordStarted(r { doEveryHourAndNow(r grabResults) }); } allServers { S lotteryURL = "https://www.lotteryusa.com/missouri/show-me-cash/"; void grabResults enter { LotteryResult r = scrapeLotteryResults(loadPage(lotteryURL)); if (r == null) ret with print("No lottery results!?"); CLotteryResult lr, bool isNew = unpair uniq2(CLotteryResult, date := r.date); copyFields(r, lr); change(); print("Grabbed: " + lr + ", new: " + isNew); if (isNew) postLastResult_allServers(); } void postLastResult_allServers { L guilds = discord.getGuilds(); for (Guild guild : guilds) pcall { ByServer bs = getByServer(guild); if (bs != null) bs.postLastResult(); } } S renderResult(CLotteryResult r) { ret formatCSVLine(flattenList2(r.date, r.numbers, r.jackpot)); } CLotteryResult latestResult() { ret first(conceptsSortedByFieldDesc CLotteryResult('date)); } } processSimplified { optPar Message msg; Message.Attachment attachment = msg == null ? null : first(msg.getAttachments()); if (attachment != null && authed(_)) { File file = prepareCacheProgramFile(guildID + "/" + attachment.getFileName()); print("Downloading attachment: " + file); deleteFile(file); if (!attachment.download(file)) ret "Couldn't download attachment"; S contents = loadTextFile(file); contents = replaceAll(contents, "^,+\r?\n", ""); if (!startsWith(contents, "\"") && !startsWithDigit(contents)) null; //ret "Attachment is not a CSV text file"; LLS l = parseCSV_unquote_noHeader(contents); l = map(l, row -> l(row) != 7 ? row : spliceList(row, 1, 6, ll(joinWithComma(subList(row, 1, 6))))); Set lengths = mapToSet(lambda1 l, l); if (neq(lengths, litset(3))) ret "CSV does not have 3 columns throughout"; for (LS line : l) { S date = first(line); if (!regexpMatches(regexpYMDminus(), date)) ret "Bad date: " + backtickQuote(date); CLotteryResult lr = uniq(CLotteryResult, +date); cset(lr, numbers := map parseInt(tok_splitAtComma(second(line))), jackpot := third(line)); } ret "OK, " + nResults(l) + " imported"; } S s0 = s; s = dropMyPrefixOpt(s); if "clear lottery results" { try answer checkAuth(_); ret "OK, " + nResults(deleteConcepts(CLotteryResult)) + " deleted"; } if "lottery|lottery results|show me cash" { optPar long channelID; L l = sortedByFieldDesc date(list(CLotteryResult)); if (empty(l)) ret "No lottery results gathered yet"; S date1 = first(l).date; S text = windowsLineBreaks(mapToLines_rtrim(l, lambda1 renderResult)); uploadFileInChannel(channelID, toUtf8(text), "lottery-" + date1 + ".csv", null, null); null; } if "Post lottery here" { long channelID = longPar channelID(_); if (channelID == 0) ret "Please do this in a channel"; if (resultsChannelID != channelID) { resultsChannelID = channelID; change(); } doAfter(2.0, r postLastResult); ret "OK, I will post new lottery results in this channel. Type `stop posting lottery` to stop."; } if "stop posting lottery" { resultsChannelID = 0; change(); ret "OK"; } if null (s = dropMyPrefixOrNull(s0)) null; if "help" { ret [[ @me `lottery` - download latest lottery results as CSV @me `clear lottery results` - delete all lottery results To add lottery results, just upload a CSV file. ]].replace("@me", atSelf()); } } void postLastResult { if (resultsChannelID != 0 && latestResult() != null) postInChannel(resultsChannelID, "Here are the latest lottery results: " + renderResult(latestResult())); } }