What I built
Magic Eight Ball is a wondrous device that lets a user offload any important decision to a plastic ball. Well, this one you don't even need to shake! I've built a GitHub Action that would allow you to tempt fate and receive answers to the most important questions right in the comments to a PR or an issue!
Category Submission:
Wacky Wildcards
App Link
Screenshots
Description
Action runs on each comment and if it starts with a 🎱
(or another defined prefix) - it will reply with a random answer.
Link to Source Code
ValeriaVG / actions-magic-8-ball
Get Magic ball answers delivered directly to your comments
GitHub Actions: Magic Eight Ball comments
Get Magic ball answers delivered directly to your comments.
How to use
Create an action in .github/workflows
:
on:
issue_comment:
types: [created]
jobs:
example_comment_pr:
runs-on: ubuntu-latest
name: Magic Eight Ball Comments
permissions:
pull-requests: write
issues: write
steps:
- name: Answer
uses: ValeriaVG/actions-magic-8-ball@v1
Post a comment in PR or and issue starting with "🎱" and get a prediction:
With custom prefix
You can also change the prefix to a custom one:
on:
issue_comment:
types: [created]
jobs:
example_comment_pr:
runs-on: ubuntu-latest
name: Magic Eight Ball Comments
permissions:
pull-requests: write
issues: write
steps:
- name: Answer
uses: ValeriaVG/actions-magic-8-ball@v1
with:
prefix: 'Magic ball, would you please'
Credits
Responses have been generated with Free Research Preview ChatGPT
Permissive License
Background
I wanted to build something magical, fun, a bit chaotic and over-the-top! I asked a magic eight ball if my fortune cookie idea was any good and it said that magic eight ball was a better one :)
How I built it
I've started with the actual magic eight ball script. The most important part was, of course, to write the answers. I've learned that a classic magic eight ball has 20 answers, but they are so boring...
I wanted something opposite of boring! Therefore I went to ChatGPT and politely asked it to generate some funny responses:
Of course, I've asked it for an algorithm as well, but it was too plain:
const responses = [
"It is certain",
"Without a doubt",
"Yes – definitely",
"You may rely on it",
"As I see it, yes",
"Most likely",
"Outlook good",
"Yes",
"Signs point to yes",
"Reply hazy, try again",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"Outlook not so good",
"My sources say no",
"Very doubtful"
];
function generateEightBallResponse() {
const randomIndex = Math.floor(Math.random() * responses.length);
return responses[randomIndex];
}
// Example usage
console.log(generateEightBallResponse());
I've asked to rewrite it with crypto.randomBytes
, because I sure wanted cryptographically secure random answers (who wouldn't? ):
function generateEightBallResponse() {
const randomBytes = crypto.randomBytes(1);
const randomIndex = randomBytes[0] % responses.length;
return responses[randomIndex];
}
This was a bit better, but one can never have enough magic and so I settled for the following overkill version ( writing it myself this time was faster than trying to explain to an AI what I had in mind ):
function generateEightBallResponse() {
const randomSize = crypto.randomBytes(1)[0];
const randomValues = new Uint32Array(randomSize);
crypto.getRandomValues(randomValues);
const randomIndex = randomValues[Math.floor(Math.random() * randomSize)] % responses.length;
return responses[randomIndex];
}
Now that's what I call magic!
Next step was to turn it into an actual GitHub action.
I've looked for some similar actions and it was quite easy to replicate.
I've created an action.yml
file in the root of my repository:
name: 'Magic Eight Ball'
branding:
icon: 'message-circle'
color: 'gray-dark'
description: 'Replies to a comment with a prediction'
inputs:
prefix:
description: 'Prefix that would trigger the response'
default: '🎱'
required: false
GITHUB_TOKEN:
description: 'Github token of the repository (automatically created by Github)'
default: ${{ github.token }}
required: false
runs:
using: 'node16'
main: 'lib/index.js'
Added the script to post a comment, using @actions/core and @actions/github packages:
import { context, getOctokit } from "@actions/github";
import { getInput } from "@actions/core";
import generateEightBallResponse from "./generateResponse";
const run = async () => {
const prefix: string = getInput("prefix");
const github_token: string = getInput("GITHUB_TOKEN");
if (
!context.payload.comment ||
!context.payload.comment["body"]?.startsWith(prefix)
) {
return;
}
const issue_number =
context.payload.pull_request?.number || context.payload.issue?.number;
if (!issue_number) {
return;
}
const octokit = getOctokit(github_token);
const body =
'> ' + context.payload.comment["body"] + '\n\r' +
"@" +
context.payload.comment["user"].login +
" " +
generateEightBallResponse();
await octokit.rest.issues.createComment({
...context.repo,
issue_number,
body,
});
};
run();
And created a workflow to test this action in the same repository:
on:
issue_comment:
types: [created]
jobs:
test_8ball:
name: Magic Eight Ball
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Run action from main
uses: ./
It didn't work on the first try and I've learned about permissions (originally I was missing contents: read
and my workflow was failing to checkout the repo).
Then, I've learned that default action runners support node16 at most and I couldn't use crypto.getRandomValues
just yet. With a heavy heart I've settled for a little bit less magical random generator (by using crypto.randomBytes
twice):
export default function generateEightBallResponse() {
const randomSize = crypto.randomBytes(1)[0];
const randomValues = crypto.randomBytes(randomSize);
const randomIndex =
randomValues[Math.floor(Math.random() * randomSize)] % responses.length;
return responses[randomIndex];
}
I could have used a different action and setup whatever version of node I needed, but I wanted the action to be as small and as fast as possible.
I've bundled all code with esbuild into one file and it finally it worked!
In the end I wanted to write some tests, but Magic eight ball told me not to :-P
Top comments (1)
this looks great!