
I wanted to create a small program that would send the votes made by my Hive account, @we-are-ai, to a Discord channel.
At first, I thought it would be pretty simple: retrieve my votes and send them to Discord. But the project came with quite a few surprises! đ
My first idea was to use a PeakD RSS feed. I soon realised it wasnât the right solution for tracking my accountâs votes, so I had to find another way to retrieve Hive operations directly.
Since I use WampServer, I wanted to run the PHP program locally on my computer, which stays on all the time. The code could retrieve the information, but connecting to the Hive API caused problems because of the SSL certificate.
I had to find the cacert.pem file and then update PHPâs configuration in the php.ini file, which I accessed through WampServerâs menus. That wasnât exactly easy when youâre not familiar with those settings!
Next, I had to get the messages working through a Discord webhook. The page could detect votes, but the messages didnât always appear in the channel. I checked the webhook, tested several versions of the code, and went through the setup more than once.
I wonât lie: I lost my temper several times in front of the screen! đ Between the errors, pages loading forever, and changes that seemed to break everything, there were a few frustrating moments.
I also wanted to stop the program from sending the same votes over and over. The code therefore remembers the last operation it processed, so it can try to send only new ones.
I then added an automatic refresh every 60 seconds, with a visible countdown until the next reload.
After lots of testing, fixes, and a few moments of frustration, it finally works: the program detects new votes and sends them to Discord. The page also displays a countdown before the next check.
It may have been a small project, but it turned into quite an adventure for me. When youâre just starting out, every detailâPHP, WampServer, the SSL certificate, or the webhookâcan quickly become a real headache. But I finally got there! đ
Iâll share the final code at the end of the post, without my webhook. Youâll need to replace the placeholder with your own webhook. Be careful: a webhook is a secret URL that allows messages to be sent to its associated channel. Donât publish it.
// Replace this with your own webhook, and never share it publicly:
$discordWebhook = 'PASTE_YOUR_WEBHOOK_HERE';
The famous code
<?php
ini_set('display_errors', '1');
error_reporting(E_ALL);
$account = 'we-are-ai';
$hiveUrl = 'https://api.hive.blog';
$discordWebhook = 'PASTE_YOUR_WEBHOOK_HERE';
$stateFile = __DIR__ . '/votes_state.json';
function postJson($url, $payload)
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20
]);
$response = curl_exec($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [$response, $error, $httpCode];
}
// Retrieve the latest Hive account operations
$payload = [
'jsonrpc' => '2.0',
'method' => 'condenser_api.get_account_history',
'params' => [$account, -1, 1000],
'id' => 1
];
[$response, $error, $httpCode] = postJson($hiveUrl, $payload);
if ($response === false || $error !== '') {
die('Hive connection error: ' . htmlspecialchars($error));
}
$data = json_decode($response, true);
if ($httpCode !== 200 || !isset($data['result']) || !is_array($data['result'])) {
die('Invalid response from Hive:<pre>' . htmlspecialchars($response) . '</pre>');
}
$history = $data['result'];
// On the first run, save the current position without sending old votes.
if (!file_exists($stateFile)) {
$latestIndex = -1;
foreach ($history as $entry) {
if (isset($entry[0]) && $entry[0] > $latestIndex) {
$latestIndex = (int)$entry[0];
}
}
file_put_contents($stateFile, json_encode(['last_index' => $latestIndex]));
die('Initialization complete. No previous votes were sent. Reload the page to check again.');
}
$state = json_decode(file_get_contents($stateFile), true);
$lastIndex = (int)($state['last_index'] ?? -1);
// Sort operations from oldest to newest
usort($history, function ($a, $b) {
return ($a[0] ?? 0) <=> ($b[0] ?? 0);
});
$sent = 0;
foreach ($history as $entry) {
$index = (int)($entry[0] ?? -1);
if ($index <= $lastIndex) {
continue;
}
$operation = $entry[1]['op'] ?? null;
if (
is_array($operation) &&
($operation[0] ?? '') === 'vote'
) {
$vote = $operation[1];
$author = $vote['author'] ?? '?';
$permlink = $vote['permlink'] ?? '?';
$weight = $vote['weight'] ?? '?';
$timestamp = $entry[1]['timestamp'] ?? '';
$message = "đłď¸ New vote from **@{$account}**\n"
. "Post: https://peakd.com/@{$author}/{$permlink}\n"
. "Weight: {$weight}\n"
. "Date: {$timestamp}";
[$discordResponse, $discordError, $discordCode] = postJson(
$discordWebhook . '?wait=true',
['content' => $message]
);
if ($discordError !== '' || $discordCode < 200 || $discordCode >= 300) {
die('Discord error (HTTP ' . $discordCode . '): '
. htmlspecialchars($discordError ?: $discordResponse));
}
$sent++;
}
// Save each processed operation to prevent it from being sent again.
$lastIndex = $index;
file_put_contents($stateFile, json_encode(['last_index' => $lastIndex]));
}
echo $sent > 0
? $sent . ' new vote(s) sent to Discord.'
: 'No new votes to send.';
?>
<p>Next check in <strong><span id="countdown">60</span> seconds</strong>.</p>
<script>
let seconds = 60;
const countdown = document.getElementById('countdown');
setInterval(function () {
seconds--;
countdown.textContent = seconds;
if (seconds <= 0) {
window.location.reload();
}
}, 1000);
</script>

On Discord, go to your server and open the channel where you want your vote RSS feed to appear. Click Edit Channel (the gear icon), then Integrations, and choose Create Webhook. Copy the webhook URL and paste it into the code.
Posted Using INLEO
Update
Replace this with that
if (
is_array($operation) &&
($operation[0] ?? '') === 'vote'
) {
Otherwise, it will also display votes that other people make on your posts.
if (
is_array($operation) &&
($operation[0] ?? '') === 'vote' &&
($operation[1]['voter'] ?? '') === $account
) {
Come on, little bot, you're supposed to spot my !GLYPH .. oh well, we'll look into that later.