June 18, 2025
I added a simple d6 dice roll that uses the random library, with logging and options to post in channel. I also created a boilerplate template that I can use for new commands that just requires changing a single variable (which is the command name). The dice roll firsts generates a random number between 1 and 6, checks the args (show in channel or visible to the user only) and if it contains true, it posts the random number in the channel. If it is anything else, it makes it so only the sender can see it (they're called ephemeral messages)
I made a way to collapse the options because they can get really messy and in the way on small screens. In theory, it should have been simple, it's a button that changes the css of the div holding the dropdowns. I also wanted to make it save whether or not it was visible, so if you reloaded the page, it would stay the same. However, Flask is really slow at updating the session (the cookie), so if you reloaded the page and pressed the button within around 3 seconds, it wouldn't update (since i was using a post route that only updates it after it returns and finishes). Fortunately, I found out you could use session.modified = True
to make it change and that's why it took me so long lol :D
I added the time machine feature! Basically, my main function uses a helper script to get all of the backups in a directory (hosted in /static) and show them in a menu. You can select a date and it will save it in your session (or cookie) just like the shop and dates. Unfortunately, the older backups are not compatible as i used a different structure (hence why the 20th is empty in the video) but i'm slowly building up a catalog of backups. Also I learnt a really important lesson lol, i need to MAKE MY CODE MODULAR, because it took me an hour when it should have been 10 minutes to implement this ðŸ˜
I tried to make a script that checks if a user is a channel manager but it turns out that there isn't a way to get this using documented apis unless you have slack enterprise, which Hack Club doesn't 😠While doing this, I also moved the logic into modular programs, with commands being separate and a single main program that loads everything at once. This program handles environment variables and reduces the need to run multiple scripts at once. Also I found a message in the Slack about a method of finding channel managers involving an admin cookie, so I will try to implement that next, but for now, it only works if the id of the user running the commands is mine or the channel is #obobs-duck or #bot-spam
I started to lay the foundations for the time travel / archive feature. I changed the backup script to not copy images anymore and instead save them directly to static
(since there is no reason not to). I also created a new function in the helper program that gets the latest backups, which retrieves all of the existing backup apis in static, which I will use to show a dropdown to select a date
I made some small quality of life improvements including fixing the website title, adding a favicon and fixing some bugs (a result of using quotes inside of quotes). Finally, I added some meta and opengraph tags to the rendered html, so platforms like slack can preview orpheusmarket better, with a description, title and image! Check the image for a better idea of what I'm talking about :D I also made it so that the backup script deletes the oldest backups to keep the count within 30 (only on nest because i have only 15gb of storage, I will keep all of the old backups on my raspberry pi, for the future time travel feature).
wow! This took me a while, but basically all of the images are locally hosted instead of being on the hc cdn (in case it breaks again ðŸ˜)! I had to change all of the paths in the backups script (because it was originally made for my rasperry pi, which runs linux) using os
so it works on any platform (Posix or NT). I also modified it to save a new JSON file named images.json which contains all of the data for the main flask application to find images and their location. Since I'll probably set a cron job to run the backup script every 12 hours, I added a fallback, so if the image doesn't exist, it just uses the cdn image. tysm for actually reading!
Mahad changed the API a bit to account for the stickerlode so I had to fix my script (basically change if item["blackMarket"] == True
to if item["shopType"] == "blackMarket
). I also added another dropdown which allows you to browse the regular SOM shop in the same website (it's also much faster than the SOM website because most of the data is cached). I had to make some changes to the css, which involved a media query (@media (min-width: 768px) {}
to make the dropdowns stacked because smaller screens can't accommodate the space needed for them being side by side. Next, I will probably either add support for stickerlode or have a time machine function where you can see stock for a particular date (courtesy of my backups lol). Thanks for reading :D
I implemented a backup system because last week, when I was testing this, both @mahad
's api and the Hack Club cdn were down :( I originally had it running on my raspberry pi and it does something similar to the actual app. It makes a request to @mahad
's api, iterates over everything and saves the images to a directory with a timestamped name. The helper script that actually gets the black market items checks if the api returns a abnormal HTTP code (anything other than 200) and if it does, it runs another function to get the path of the latest backup and then loads that!
I made the sticky note actually stick! Basically, it has a function that runs whenever a message is sent in a channel that it's in and the bot checks if there is a sticked message, then saves the text from it, deletes it and sends it again. The first implementation wasn't the best because it would be called multiple times on different threads so it would bug out, post the same message twice and just wasn't very efficient with a new thread for every message. To fix this, I used the threading library to implement locks for channels and a debounce of 1 second so if a channel is really busy it doesn't waste lots of resources and won't bug out! ty for reading :D
I started making the sticky notes feature which basically sticks a message to the bottom of your channel. Right now, though, it doesn't actually pin it (it just acts like a normal message and if another message is posted in the channel, it doesn't actually stick ðŸ˜), which will be what i do in the next devlog.
To actually make this feature, I created a slash command (/sticky-note
) which has 3 actions, create, edit and delete. I used the Slack Bolt library with socket mode so i don't need a https endpoint and created a script that basically listens for the slash command. Then it splits the command into 2 parts, the action (the first keyword) and the message. If the action is valid (create, edit or delete) then it will do the action and if not, it will display an ephemeral message to the user with a usage guide. I also used sqlite3 to store the messages (channel_id and timestamp to be more specific because that's how slack keeps track of messages) in a database so it still remembers messages after a restart. and uhh sorry for this long devlog lol :D
I read the slack docs and created a simple script with the slack_bolt library to respond to the message hi in my testing channel
a slack bot that i'm making to help with some stuff
nice project works well, atleast I didn't actually bought something lol, but BM is for PIRATES, HAHAHAH c3
I created a readme and banner using inkscape!
I styled the button with css to make it bigger and put it into a card, like the items. I also added a picture of orpheus on the top bar, and some css (overflow-x: hidden; and max-width: 100vw;) to prevent horizontal scrolling, a really annoying issue that I have come across many times when scrolling through websites on a phone, which you can see near the end of the video. Also, the dropdown didn't appear in the video (it does appear in the actual website) for some reason, so ignore that
I added lots of CSS to the cards and replicated the theme of heidimarket. Basically, I just have a variable where I add CSS and inject that into a <style> tag. I also added a region dropdown that allows you to get the correct cost for your region. When you change your selection on the dropdown, it adds ?region=regionhere and reloads the page. If the region is a valid region (US, EU, IN, CA, AU or XX) and then changes the items displayed to you and your cookie (so it stays persistent)
I created the basic webpage using Flask. It uses my script from the previous devlog (but modified to print anything) to get the items which are then iterated over and made into a card as seen on line 20-25. I just created a really simple boilerplate for the html and added {{card_html|safe}} in the body to render all of the cards. I had to use the safe option, else it would have been escaped by flask and wouldn't work. Right now, I have the images, buy links, title and description. However, I still need to add proper CSS because right now it looks like a webpage from the 2000s lol
I created the first basic script to get the items from @mahad's api, split them into blackmarket and the normal shop and display them with the shell count. I used the Colorama module for the colored output and the requests library to get the api. There is a spaces variable that is set to 5 and subtracted by the length of the shell cost. This just makes everything aligned and is by far the most overengineered part so far. The script also gets the regions from the api and puts it all in a set, so you can get accurate costs for your region
a fake blackmarket site with the actual stock and products in the real black market! built with flask and mahad's api!
I created a simple AI detector that uses the features as described in the previous devlog. For now it works well for what I need it to, but I might change train a model if I need to in the future. There is a new script called getDevlogs which basically fetches the text from each devlog in a project (in my database, each project has an array of devlogs which I iterate over in the script). Then it runs my classifier which basically uses math to calculate the AI probability. Then the mean of the scores are calculated and that is the final percentage which you can see near the end of the video
I created a python script that extracts the important features of the input text (either text or code). I will then later use this to calculate the probability of it being AI without using AI to calculate. So far it returns the number of words, number of sentences, mean sentence length (using numpy), buzzword ratio (using a list of buzzwords provided in a txt file) and the punctuation ratio
I wrote a readme with installation instructions and a demo video!
I added a better UX design for both modes and a HUD alert now shows when the extension has finished converting. When you open the extension, an interval shows a Loading clipboard message with the number of dots at the end changing. After your clipboard has loaded, then the output is saved to your clipboard, the window closes, and a small HUD alert will show (which you can see in the video)
This will probably be my final devlog for this, and it was very fun to make (my first time using react :D) Also side note, the raycast api is really easy to use (for some reason I was expecting it to be overly complex lol)
I implemented HTML to Markdown so now you can go both ways. Since marked can't convert HTML to Markdown, I am using turndown.
The extension also now copies the result to your clipboard, however that has the result of immediately closing it, so in the next devlog I will deal with that and make it look better
I implemented markdown to HTML conversion using the marked library. It first gets the clipboard using the raycast clipboard api, then it trims the result, checks if it isnt null or undefined and gives it to marked. Then it returns the original rendered markdown and the raw html output in a code block so you can actually see the tags
I created the basic function to get the contents of your clipboard using the Clipboard.readText() function from the Raycast API. It uses the useState and useEffect hooks and runs the Clipboard call in async so I can use await. It also handles abruptly exiting simply using a variable named cancelled. Next, I will convert the returned markdown in the clipboard, convert it into html and copy it to the clipboard, probably by using Clipboard.copy()
I added linux arm support to the prebuilt pyinstaller binaries so u-crawler can run on my raspberry pi :D
I also had to set the github actions runner to run bookworm instead of the latest debian version. Otherwise, older versions of debian distros wouldn’t work (see the attached image) as they are packaged with an older version of glibc (the c library that pyinstaller uses)
I fixed some errors that might have caused issues on other devices. For example, I changed the selenium headless flag to the older one that still works, disabled disable gpu and remade the spec file to be more efficient and only include modules that I actually need. The attached screenshot of it working on my device, and I also tested it on 2 other laptops so it should work now
A Raycast extension that converts Markdown to HTML and vice versa. I used the marked.js and turndown libraries to convert the text into HTML and Markdown, and Clipboard.read() from the Raycast api to get the original text.
I created a program that searches the MongoDB database from my previous devlogs and prints out the results. It uses fuzzy search (the $search operator that can be seen in the code in the video) which basically also returns matches even if they aren't exactly the same as the query. It then sorts it by relevance and includes pagination using this line:
skipcount = (page - 1) * pagesize
which is then passed to the project_collection.find() method
I changed the script to use async which fetches data concurrently and is much faster! (30 minutes down to around 3 minutes!!!) It also now uses bulk_write with mongodb, which is much more efficient and reduces the amount of connections needed.
I also got a nest account, which is where I will be hosting the backend (my next step is to make the front end)
I deployed to github pages and fixed some hyperlink issues
I improved the css alot! I added a font from google fonts, a background and custom text formatting
I added a really really simple contact page
I added the projects page with a div for each project so you can simply copy and paste and have new card!
I finished making the homepage which shows some of my interests and projects
I created the first version of the homepage. It includes a footer, my profile picture as a clickable button and some info about me
I made a very simple 404 page (css coming soon)
A personal website I made for the Swirl ysws, which features my projects and some cool stuff
I created a python script that uses the SOM projects api (https://summer.hackclub.com/api/v1/projects) to update a MongoDB collection with information about every project in Summer of Making. This data in the data base will then be used to calculate the estimated shell profit
I added a dialogue system using the dialogic plugin and a new movement system which has double jumps, slams and more!
I added a health bar and an artifact bar that uses art I made in Aseprite. I also added helpful debugging messages and a base scene that contains all of the required components such as the health bar and killzone so I don't have to add them manually every time I create a new level
I made a gem that will be used to show the amount of artifacts collected like a health bar in game
I created a big todo markdown file to keep track of what I need to do. It includes stuff like health, music and level design
I created an option in the next_scene node to decide whether to check the amount of artifacts collected or not (it is only checked in the second level of a time period). I also made a second level to test it
I added a scene that decides whether to move you to the next time period depending on the number of artifacts you have collected. Each time period will have 4 artifacts, and you only need 3 to progress
I added a collectible system that can be used for artifacts in each time period.
I created a basic movement script with coyote time and double jumps. It also features easily changeable variables (such as speed and jump strength) to customise the feel of movement later
I finished the login flow and the function to get user details
A website that uses the spotify api to tell you the "mood" of your playlists
Now my scraper uses the results of categories.json to crawl every program inside of that category. Instead of simply using requests and beautiful soup like I did for the category scraper, I had to use selenium to launch a headless browser, because the data for the programs is rendered with JavaScript. In the the screenshot below, you can see the format of the results with categories.json and the programs in each category as their own file and with some of the code on the right.
A 2D platformer where you go through time to collect artifacts
u-crawler is a web scraper that utilizes the BeautifulSoup, requests and selenium to collect data about courses and programs from the University of New South Wales. It adheres to robots.txt and outputs its results into a neatly formatted directory composed of json files. I created this for the Anansi YSWS and I learnt a lot about web scraping :D
I added an idle animation that activates after 30 seconds of inactivity and a startup animation that activates when the board powers on.
I also added feedback to the reaction game, if you get < 230 ms, the LEDs will blink quickly, for < 350 ms they will blink normally and longer than that, they will blink slowly.
I added a new game-binary!
How it works:
- The LEDs will show a binary number for 5 seconds
- When they turn off, you have to input that value using the keyboard
- Pressing the top left button adds 1
- Pressing the top right button adds 5
- Pressing the bottom left button subtracts 1
- To submit your number, hold the bottom right button for 2 seconds
- If you got it correct, the LEDs will all flash twice
- If you got it wrong, they will flash one by one 2 times
I added more silkscreen art to the PCB, thank you solder people for helping
I made the circuit on a breadboard, and added a resistor to make the sensitivity potentiometer actually work. I'm probably ready to ship now!
Added a keychain hole and some silkscreen art.
I made the first version of my pcb design! it's supposed to be a cute duck keychain, but now that I think about it, I forgot the hole lol.
I finished my schematic! I'm planning on making it turn on in the dark (with the photoresistor) and off in the light.
A PCB with lights that you can use as a keychain.
This was widely regarded as a great move by everyone.