<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tariq Mehmood</title>
    <description>The latest articles on Forem by Tariq Mehmood (@leonardokaprio).</description>
    <link>https://forem.com/leonardokaprio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1944122%2Fbfc01ca9-9ffb-4c2e-9276-57b81b0d3102.png</url>
      <title>Forem: Tariq Mehmood</title>
      <link>https://forem.com/leonardokaprio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/leonardokaprio"/>
    <language>en</language>
    <item>
      <title>Building a live electricity dashboard as a first-semester CS student</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Fri, 27 Mar 2026 04:49:32 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/building-a-live-electricity-dashboard-as-a-first-semester-cs-student-59i8</link>
      <guid>https://forem.com/leonardokaprio/building-a-live-electricity-dashboard-as-a-first-semester-cs-student-59i8</guid>
      <description>&lt;p&gt;Hi everyone, I’m Tariq Mehmood. I’m currently in my first semester as a computer science student, and for the past few months, I’ve been building my first major production project: a Finnish electricity price-tracking website Sähkötänään.&lt;/p&gt;

&lt;p&gt;In my university classes, we are diving deep into C++, memory management, and core algorithms. But applying those foundational concepts to a live web application with real-time data fetching has been an entirely different beast.&lt;/p&gt;

&lt;p&gt;I wanted to share a bit about the architecture hurdles I’m facing and see how more experienced devs handle the transition from "student projects" to scalable tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sahkotanaan.fi/" rel="noopener noreferrer"&gt;Sähkötänään&lt;/a&gt; is designed to help Finnish consumers track fluctuating exchange electricity (spot) prices. The core feature is a live data dashboard that pulls hourly pricing via API and feeds that data into interactive calculators (e.g., calculating the exact cost of running a sauna or home heating based on the current hour's rate).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture Hurdle: Managing Live Data
&lt;/h2&gt;

&lt;p&gt;Initially, I thought building a dashboard just meant making a fetch() call and rendering a chart. The reality hit me hard when &lt;/p&gt;

&lt;p&gt;I started dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limits &amp;amp; Caching:&lt;/strong&gt; I can't hammer the regional energy APIs every time a user loads the page. I'm currently working through the best ways to cache that hourly data on my backend and serve it efficiently to the frontend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management in Calculators:&lt;/strong&gt; The calculators need to instantly react to both user inputs (like their specific contract margin) and the live API data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AI Assist:&lt;/strong&gt; As the logic got heavier, I started integrating AI tools (like Codex) to help scaffold the core data-handling logic while I focused on the overall architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Pivot: From Solo Dev to Technical Manager
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I've learned recently isn't even about code. It’s about scope.&lt;/p&gt;

&lt;p&gt;To make the site an authoritative resource, it needs content—articles on optimizing home heating, contract comparisons, and daily price analyses. I quickly realized I couldn't write hundreds of articles, run a forum outreach campaign, and build a scalable backend at the same time.&lt;/p&gt;

&lt;p&gt;I recently brought on a small team of three writers and an outreach specialist. Writing Standard Operating Procedures (SOPs) for them to ensure quality without relying on spammy AI content was my first real taste of being a technical founder. Delegating the content means I can finally step back and focus entirely on the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions for the Community
&lt;/h2&gt;

&lt;p&gt;As I continue to build out the backend to support this new influx of traffic and content, I’d love to hear from those who have built similar data-heavy dashboards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caching Strategies:&lt;/strong&gt; What is your go-to architecture for caching and serving third-party hourly API data without over-engineering it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration:&lt;/strong&gt; For those using AI to assist with coding, do you prefer to have the AI write your core API logic, or do you handle the data yourself and use AI strictly for frontend components?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling:&lt;/strong&gt; Any advice for a student on making sure a newly launched dashboard doesn't immediately crash when traffic spikes?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>api</category>
    </item>
    <item>
      <title>Is the RBT Exam Difficult?</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Sun, 01 Mar 2026 11:01:26 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/is-the-rbt-exam-difficult-4o6g</link>
      <guid>https://forem.com/leonardokaprio/is-the-rbt-exam-difficult-4o6g</guid>
      <description>&lt;p&gt;If you’re preparing for the RBT exam, you’re probably wondering: Is it actually hard? The honest answer is: it depends on how prepared you are — but for most candidates, the &lt;a href="https://therbtpracticeexam.com/" rel="noopener noreferrer"&gt;RBT practice exam free&lt;/a&gt; is very manageable with focused study.&lt;/p&gt;

&lt;p&gt;Let’s break it down clearly so you know exactly what to expect and how to approach it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes People Think It’s Difficult?
&lt;/h2&gt;

&lt;p&gt;Here are the most common reasons candidates feel nervous:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Terminology
&lt;/h3&gt;

&lt;p&gt;ABA uses specific language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reinforcement vs. punishment&lt;/li&gt;
&lt;li&gt;Positive vs. negative&lt;/li&gt;
&lt;li&gt;Continuous vs. intermittent reinforcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t clearly understand definitions, questions can feel tricky.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Scenario-Based Questions
&lt;/h3&gt;

&lt;p&gt;The exam doesn’t just ask definitions. It gives real-world scenarios and asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should the RBT do?&lt;/li&gt;
&lt;li&gt;What is the correct procedure?&lt;/li&gt;
&lt;li&gt;What ethical guideline applies?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This requires understanding, not memorization.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Ethics Section
&lt;/h3&gt;

&lt;p&gt;Questions about professional conduct, supervision, and boundaries can be subtle. Many test-takers underestimate this part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Makes It Manageable
&lt;/h2&gt;

&lt;p&gt;Here’s the good news: the RBT exam is considered an entry-level certification.&lt;/p&gt;

&lt;p&gt;You are not expected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design treatment plans&lt;/li&gt;
&lt;li&gt;Conduct functional behavior assessments independently&lt;/li&gt;
&lt;li&gt;Make high-level clinical decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are expected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement behavior plans&lt;/li&gt;
&lt;li&gt;Collect data correctly&lt;/li&gt;
&lt;li&gt;Follow ethical guidelines&lt;/li&gt;
&lt;li&gt;Communicate professionally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you completed your 40-hour training and reviewed the task list carefully, you already have most of what you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Typically Causes People to Fail?
&lt;/h2&gt;

&lt;p&gt;Not studying strategically.&lt;/p&gt;

&lt;p&gt;Common mistakes:&lt;/p&gt;

&lt;p&gt;Memorizing flashcards without understanding concepts&lt;/p&gt;

&lt;p&gt;Ignoring ethics&lt;/p&gt;

&lt;p&gt;Not practicing scenario questions&lt;/p&gt;

&lt;p&gt;Cramming the night before&lt;/p&gt;

&lt;p&gt;Not taking practice exams&lt;/p&gt;

&lt;p&gt;How to Make It “Not Difficult”&lt;/p&gt;

&lt;p&gt;Here’s a simple plan:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Master the Task List
&lt;/h3&gt;

&lt;p&gt;Study each section:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Measurement&lt;/li&gt;
&lt;li&gt;Assessment&lt;/li&gt;
&lt;li&gt;Skill Acquisition&lt;/li&gt;
&lt;li&gt;Behavior Reduction&lt;/li&gt;
&lt;li&gt;Documentation and Reporting&lt;/li&gt;
&lt;li&gt;Professional Conduct and Scope of Practice&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Practice Scenario Questions
&lt;/h3&gt;

&lt;p&gt;The more you see real-life examples, the easier the exam feels.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Understand Reinforcement Deeply
&lt;/h3&gt;

&lt;p&gt;Most tricky questions relate to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifying reinforcement vs. punishment&lt;/li&gt;
&lt;li&gt;Determining function of behavior&lt;/li&gt;
&lt;li&gt;Selecting correct data collection methods&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Review Ethics Carefully
&lt;/h3&gt;

&lt;p&gt;Know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When to report concerns&lt;/li&gt;
&lt;li&gt;Boundaries with clients&lt;/li&gt;
&lt;li&gt;Supervision requirements&lt;/li&gt;
&lt;li&gt;Confidentiality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So… Is It Hard?
&lt;/h2&gt;

&lt;p&gt;Here’s the balanced answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is not an extremely difficult exam.&lt;/li&gt;
&lt;li&gt;It is not something you can pass without studying.&lt;/li&gt;
&lt;li&gt;It is very passable with preparation.&lt;/li&gt;
&lt;li&gt;Most candidates succeed on their first try.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you understand the concepts rather than memorize definitions, you’ll likely do well.&lt;/p&gt;

&lt;p&gt;A Confidence Check&lt;/p&gt;

&lt;p&gt;If you can confidently answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“What is the function of this behavior?”&lt;/li&gt;
&lt;li&gt;“Is this positive reinforcement or negative punishment?”&lt;/li&gt;
&lt;li&gt;“What should an RBT do if a parent asks for treatment advice outside the plan?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re probably ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The RBT exam feels intimidating mostly because it’s your first professional certification. But it’s designed to test whether you can safely and competently implement behavior plans — not whether you’re an expert.&lt;/p&gt;

&lt;p&gt;With structured study and practice, it becomes much more about clarity than difficulty.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>frontend</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Code Brawl Stars Private Server Logic in Node.js</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Mon, 22 Dec 2025 10:49:37 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/how-to-code-brawl-stars-private-server-logic-in-nodejs-2gfi</link>
      <guid>https://forem.com/leonardokaprio/how-to-code-brawl-stars-private-server-logic-in-nodejs-2gfi</guid>
      <description>&lt;p&gt;If you’ve ever wondered what it would be like to unlock all Brawlers, enjoy unlimited gems, test custom skins, or experiment with your own game rules, then the idea of a Brawl Stars private server has probably crossed your mind. While official Brawl Stars gameplay on Supercell’s servers is locked to strict rules, many players dream of a sandbox-style experience. That’s where the concept of coding or creating a Brawl Stars private server comes in.&lt;/p&gt;

&lt;p&gt;Before moving ahead, remember that real private servers violate Supercell’s Terms of Service. This guide is only for educational and informational purposes, helping you understand how such systems work from a programming and server-logic standpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Brawl Stars Private Server?
&lt;/h2&gt;

&lt;p&gt;A Brawl Stars private server is a custom environment where the restrictions of the original game are removed. Players often get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlimited gems and coins&lt;/li&gt;
&lt;li&gt;All brawlers unlocked&lt;/li&gt;
&lt;li&gt;Custom maps and skins&lt;/li&gt;
&lt;li&gt;Faster upgrades and gameplay control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike the official Supercell servers, a private server runs on independent backend logic that mimics the game mechanics but does not connect to the real player base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do Developers Experiment With Brawl Stars Private Servers?
&lt;/h2&gt;

&lt;p&gt;Developers and tech learners love these projects because they allow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning server-client communication&lt;/li&gt;
&lt;li&gt;Understanding game backend logic&lt;/li&gt;
&lt;li&gt;Working with databases&lt;/li&gt;
&lt;li&gt;Practicing Node.js, Python, or Java server programming&lt;/li&gt;
&lt;li&gt;Experimenting with custom gameplay mechanics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why “Brawl Stars Private Server Coding” is a hot keyword in both the gaming and programming community.&lt;/p&gt;

&lt;p&gt;How a Brawl Stars Private Server Concept Works (Developer Perspective)&lt;/p&gt;

&lt;h2&gt;
  
  
  A basic private server idea involves:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Server Backend&lt;/strong&gt;&lt;br&gt;
Handles authentication, currency amounts, unlocks, and player states.&lt;br&gt;
&lt;strong&gt;A Database&lt;/strong&gt;&lt;br&gt;
Stores user profiles, unlocked brawlers, progress, battle logs, etc.&lt;br&gt;
&lt;strong&gt;Game Logic Layer&lt;/strong&gt;&lt;br&gt;
Controls matchmaking, battle rules, brawler stats, damage, and events.&lt;br&gt;
&lt;strong&gt;Communication Layer&lt;/strong&gt;&lt;br&gt;
Syncs data between game client and server using networking protocols.&lt;/p&gt;

&lt;p&gt;Example Concept: Node.js Logic for a Fake “Brawl Stars-Like” Reward Server&lt;/p&gt;

&lt;p&gt;Again, this does NOT run the real Brawl Stars client like &lt;a href="https://nulsbrawl.fr/" rel="noopener noreferrer"&gt;Nulls brawl&lt;/a&gt; it’s only a learning simulation to demonstrate how private server logic is structured.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1 — Install Node.js
&lt;/h3&gt;

&lt;p&gt;Download and install Node.js if you don’t have it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2 — Create Project Folder
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir brawl-stars-server
cd brawl-stars-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3 — Initialize Project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 4 — Install Express
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 5 — Create server.js
&lt;/h3&gt;

&lt;p&gt;Create a file named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this complete working code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const app = express();
app.use(express.json());

// Player Database Simulation
let player = {
  username: "BrawlMaster",
  trophies: 1200,
  gems: 50,
  coins: 500,
  brawlers: ["Shelly", "Nita"]
};

// Home Route
app.get("/", (req, res) =&amp;gt; {
  res.json({
    message: "Brawl Stars Private Server Concept Running",
    player
  });
});

// Add Gems
app.post("/add-gems", (req, res) =&amp;gt; {
  const gems = req.body.gems || 1000;
  player.gems += gems;

  res.json({
    success: true,
    message: `${gems} Gems Added Successfully`,
    player
  });
});

// Unlock New Brawler
app.post("/unlock-brawler", (req, res) =&amp;gt; {
  const brawler = req.body.name || "CustomBrawler";
  player.brawlers.push(brawler);

  res.json({
    success: true,
    message: `${brawler} Unlocked!`,
    player
  });
});

// Reset Account
app.post("/reset", (req, res) =&amp;gt; {
  player.gems = 0;
  player.coins = 0;
  player.trophies = 0;
  player.brawlers = ["Shelly"];

  res.json({
    success: true,
    message: "Player Reset Completed",
    player
  });
});

// Start Server
app.listen(3000, () =&amp;gt; {
  console.log("Brawl Stars Private Server Concept Running on Port 3000");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6 — Run the Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 — Test Endpoints
&lt;/h3&gt;

&lt;p&gt;Use Postman or any API tool.&lt;/p&gt;

&lt;p&gt;View Player Data&lt;/p&gt;

&lt;p&gt;GET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add Gems&lt;/strong&gt;&lt;br&gt;
POST:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/add-gems
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Body (JSON):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ "gems": 5000 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unlock Brawler&lt;/strong&gt;&lt;br&gt;
POST:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
http://localhost:3000/unlock-brawler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ "name": "Spike" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reset Player&lt;/strong&gt;&lt;br&gt;
POST:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;A Brawl Stars private server is a dream playground where you can unlock everything, customize gameplay, and explore endless creativity. While real private servers are not legal to host or distribute, learning how such systems work from a coding perspective is incredibly valuable.&lt;/p&gt;

&lt;p&gt;If you’re passionate about gaming and programming, experimenting with Brawl Stars private server logic using Node.js or any backend technology is an amazing way to level up your skills — without breaking any rules.&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>Building a New York Times Connections Solver with Python</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Sat, 08 Nov 2025 05:03:41 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/building-a-new-york-times-connections-solver-with-python-5b8j</link>
      <guid>https://forem.com/leonardokaprio/building-a-new-york-times-connections-solver-with-python-5b8j</guid>
      <description>&lt;p&gt;If you’re a fan of word games, you’ve probably come across the puzzle from The New York Times called Connections. In this game you’re shown 16 words and you must split them into 4 groups of 4, each group sharing a hidden common theme.&lt;/p&gt;

&lt;p&gt;Recently I built a Solver for this puzzle (or at least a helper tool) and in this post I’ll walk through how I approached it, the code that underlies it, and the challenges &amp;amp; trade-offs you’ll face when building something like this. If you’ve got your own favorite puzzle solver tools, I hope this inspires you to build one too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is NYT Connections &amp;amp; why build a solver?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The puzzle
&lt;/h3&gt;

&lt;p&gt;Here’s a quick summary of the &lt;a href="https://connections-hints.today/" rel="noopener noreferrer"&gt;NYT Connections&lt;/a&gt; mechanics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have 16 words, in a 4x4 grid. &lt;/li&gt;
&lt;li&gt;You must guess 4 sets of 4 words each, where each set shares some semantic (or playful) connection. &lt;/li&gt;
&lt;li&gt;The categories are hidden, and each category is assigned a difficulty colour: yellow (easiest) → green → blue → purple (hardest). &lt;/li&gt;
&lt;li&gt;You get a small number of guesses (four “lives”) to get all four groups. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why build a solver?
&lt;/h3&gt;

&lt;p&gt;There are several motivating factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even though the puzzle looks simple, the hidden categories and subtle mis-directions (words that could belong to more than one group) make it hard. As one commentary put it, the game “makes you feel bad” because you keep tripping on the ambiguous connections. &lt;/li&gt;
&lt;li&gt;raphkoster.com&lt;/li&gt;
&lt;li&gt;Building a solver is an interesting programming challenge: how do you algorithmically detect which words belong together? How do you prioritise semantically-strong clusters vs wordplay?&lt;/li&gt;
&lt;li&gt;It gives you a chance to explore natural-language processing (NLP) heuristics, embeddings, lexical resources, and design trade-offs between “fully automated solve” vs “hint-assisted helper”.&lt;/li&gt;
&lt;li&gt;For me, I wanted a tool I could use when I’m stuck on a day’s puzzle—but also reflectively learn from (so I get better at the game).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Approach: High-level design
&lt;/h2&gt;

&lt;p&gt;Here is the architecture I settled on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Input parsing – read the 16 words for the day (either manually entered, or scraped from a source).&lt;/li&gt;
&lt;li&gt;Feature extraction – for each word, compute semantic embeddings (via a pre-trained model), lexical attributes (like word length, part-of-speech, morphological features), and possibly co-occurrence / category-likeness features.&lt;/li&gt;
&lt;li&gt;Candidate grouping – generate candidate groupings of 4 words from the 16 (combinatorial: C(16,4)=1820 possibilities), then evaluate each grouping via a scoring function that estimates “how likely these 4 share a theme”.&lt;/li&gt;
&lt;li&gt;Backtracking / covering – once you pick one group, you remove those 4 words from consideration and repeat on the remaining 12, 8, then 4. Use heuristic search to pick the best covering of the 16 into 4 groups.&lt;/li&gt;
&lt;li&gt;Ranking &amp;amp; hint output – output the best groupings, optionally highlight which category is likely easiest/hardest, or even reveal one word from the category as a “hint”.&lt;/li&gt;
&lt;li&gt;User interface – a simple web UI (or CLI) where the user can paste the 16 words, press “Solve” or “Hint”, then see suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is a rough roadmap to the code segments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code walkthrough
&lt;/h2&gt;

&lt;p&gt;I’ll use Python for the core solver logic + a minimal Flask (or FastAPI) web front-end. For embedding I’ll use the sentence-transformers library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup &amp;amp; imports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# solver.py
import itertools
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity

# Optional: morphological / lexical features
import nltk
from nltk.corpus import wordnet as wn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install via pip install sentence-transformers flask nltk sklearn etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature extraction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Initialize embedding model
embed_model = SentenceTransformer('all-mpnet-base-v2')

def embed_words(words: list[str]) -&amp;gt; np.ndarray:
    """Compute embedding vectors for each word."""
    return embed_model.encode(words, convert_to_numpy=True, normalize_embeddings=True)

def lexical_features(word: str) -&amp;gt; dict:
    """Compute simple lexical features (length, synsets, POS etc)."""
    # For simplicity we just count synset counts
    synsets = wn.synsets(word)
    return {
        'length': len(word),
        'synset_count': len(synsets),
        # you can add more ...
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These features help us measure “just because the words are close in meaning” and also “how lexically connected” they might be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoring a candidate group
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def score_group(words: list[str], embeddings: np.ndarray, idx_map: dict) -&amp;gt; float:
    """
    Score how well these four words form a coherent group.
    Higher score = more likely “correct group”.
    Using cosine similarity among embeddings + lexical heuristics.
    """
    idxs = [ idx_map[w] for w in words ]
    sub_emb = embeddings[idxs]
    # compute pairwise cosine similarities
    sim_mat = cosine_similarity(sub_emb)
    # we only care off-diagonal elements
    n = len(words)
    sims = []
    for i in range(n):
        for j in range(i+1, n):
            sims.append(sim_mat[i,j])
    avg_sim = np.mean(sims)
    # lexical heuristics: e.g., if lengths are similar, more likely
    lengths = [ len(w) for w in words ]
    length_std = np.std(lengths)
    lex_score = 1.0 / (1.0 + length_std)  # smaller std → higher score
    # final combined score (weights tuned empirically)
    return avg_sim * 0.7 + lex_score * 0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What the above does: groups whose words are semantically close (high embedding cosine similarity) score better; also groups of words with similar length (or similar lexical shape) get a small bonus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating candidates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def generate_all_groups(words: list[str], embeddings: np.ndarray) -&amp;gt; list[tuple[list[str], float]]:
    idx_map = { w : i for i,w in enumerate(words) }
    candidates = []
    for combo in itertools.combinations(words, 4):
        s = score_group(list(combo), embeddings, idx_map)
        candidates.append((list(combo), s))
    # sort descending
    candidates.sort(key=lambda x: x[1], reverse=True)
    return candidates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selecting cover sets of 4 groups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def pick_best_cover(words: list[str], embeddings: np.ndarray, top_k=50):
    """
    From all candidate groups, pick top_k best,
    then try to pick combinations of them that cover the 16 words without overlap.
    Return best covering (list of 4 groups).
    """
    candidates = generate_all_groups(words, embeddings)[:top_k]
    best_cover = None
    best_score = -1
    # Try all combinations of 4 groups from top_k
    for groups in itertools.combinations(candidates, 4):
        group_words = [ tuple(g[0]) for g in groups ]
        # flatten and check overlap
        all_words = sum(group_words, ())
        if len(set(all_words)) == 16:
            # compute combined score
            score = sum(g[1] for g in groups)
            if score &amp;gt; best_score:
                best_score = score
                best_cover = groups
    return best_cover, best_score
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above brute-forces combinations of top candidate groups (limited to top_k) to find a cover of the 16 words into 4 non-overlapping groups. In practice you might need to prune, add heuristics and cutoffs if top_k is large.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def solve_puzzle(words: list[str]):
    embeddings = embed_words(words)
    cover, score = pick_best_cover(words, embeddings, top_k=100)
    if cover is None:
        print("No non-overlapping cover found in top candidates; try increasing top_k.")
        return None
    # Return groups in descending score order
    groups_sorted = sorted(cover, key=lambda x: x[1], reverse=True)
    return [ g[0] for g in groups_sorted ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Web interface (Flask)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.py
from flask import Flask, request, render_template
from solver import solve_puzzle

app = Flask(__name__)

@app.route('/', methods=['GET','POST'])
def home():
    if request.method == 'POST':
        text = request.form['words']
        words = [ w.strip() for w in text.split(',') if w.strip() ]
        if len(words) != 16:
            return render_template('index.html', error="Please enter exactly 16 comma-separated words.")
        groups = solve_puzzle(words)
        return render_template('index.html', groups=groups, original=words)
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the HTML template (templates/index.html) might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;NYT Connections Solver&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;NYT Connections Solver&amp;lt;/h1&amp;gt;
  {% if error %}
    &amp;lt;p style="color:red"&amp;gt;{{ error }}&amp;lt;/p&amp;gt;
  {% endif %}
  &amp;lt;form method="post"&amp;gt;
    &amp;lt;textarea name="words" rows="4" cols="80" placeholder="Enter your 16 words, comma separated"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;button type="submit"&amp;gt;Solve&amp;lt;/button&amp;gt;
  &amp;lt;/form&amp;gt;
  {% if groups %}
    &amp;lt;h2&amp;gt;Suggested Groups&amp;lt;/h2&amp;gt;
    &amp;lt;ol&amp;gt;
    {% for grp in groups %}
      &amp;lt;li&amp;gt;{{ grp | join(', ') }}&amp;lt;/li&amp;gt;
    {% endfor %}
    &amp;lt;/ol&amp;gt;
  {% endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What this solver achieves &amp;amp; its limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Achievements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It automates the tedious enumeration of candidate 4-word sets and ranks them by a plausible score.&lt;/li&gt;
&lt;li&gt;It supports a hint mode: if you only want to see the top1 group first, you can show just the highest-scoring group, and then reveal more if needed.&lt;/li&gt;
&lt;li&gt;It helps you learn: by seeing which groups the solver thinks are strongest, you can compare your intuition and perhaps improve your strategy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Limitations &amp;amp; caveats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Embedding similarity is not enough: Many of the hardest categories in Connections rely on subtle word-play (e.g., words that sound like letters, puns, multi-word expressions) or very specific cultural references that embeddings don’t necessarily capture. As one academic paper points out, this puzzle is “a deceptively simple text classification task that stumps system-1 thinkers”. &lt;/li&gt;
&lt;li&gt;arXiv&lt;/li&gt;
&lt;li&gt;Overfitting/False positives: The solver might pick 4 words that are “very similar” in meaning (e.g., synonyms) but that don’t match what the NYT theme intended. The theme might be less about “meaning” and more about “sound”, or “prefix/suffix”, or “it appears in a phrase”.&lt;/li&gt;
&lt;li&gt;Brute-force covering is expensive: For 16 words you can brute all combinations of 4 groups from 1820 candidate groups fairly easily, but if you wanted to extend to larger grids (say 20 words or bigger) it would scale poorly.&lt;/li&gt;
&lt;li&gt;No guarantee of uniqueness: The real puzzle has exactly one solution, but the solver might find multiple plausible covers with similar scores. The user still needs human judgment to decide which one “fits” best.&lt;/li&gt;
&lt;li&gt;No direct integration: This solver does not (and cannot legally) fetch the official NYT puzzle data automatically (without permission). So you’ll likely rely on manual entry of the 16 words or a scraping workaround (which may run afoul of terms of service).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  UX &amp;amp; user-experience considerations
&lt;/h2&gt;

&lt;p&gt;When I built the web UI I kept the following user-experience aspects in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimal friction: Paste 16 words → click Solve → get results. No login required, no complex setup.&lt;/li&gt;
&lt;li&gt;Progressive disclosure: Some users just want one hint (e.g., “give me the easiest category”). Others want the full solution. So I added a “Hint only” mode.&lt;/li&gt;
&lt;li&gt;Highlighting uncertainty: Since the solver is approximate, I indicate a “confidence score” (e.g., grouped words with score &amp;gt; 0.8 are likely) and perhaps highlight groups with lower confidence so the user knows “this one might be wrong”.&lt;/li&gt;
&lt;li&gt;Color-coding: Even though I don’t know the exact NYT colour assignment, I choose to display groups in order of descending score and label them “Likely easiest”, “Likely moderate”, etc.&lt;/li&gt;
&lt;li&gt;Mobile friendly: Many users play the puzzle on mobile; so the web UI is responsive and the textarea is thoughtfully sized.&lt;/li&gt;
&lt;li&gt;Educational value: I put an option “Explain why this group might work” which shows a small explanation (based on the lexical/embedding heuristics) so the user can learn: e.g., “These words are very close in embedding space” or “These words share a common prefix/suffix pattern”.&lt;/li&gt;
&lt;li&gt;Respect for puzzle integrity: If you embed a full “solve” mode, you are essentially spoiling the puzzle; so I also added a moral disclaimer to encourage users to try the puzzle on their own first before using full solve mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy &amp;amp; heuristics inspired by human players
&lt;/h2&gt;

&lt;p&gt;When I studied how human players play Connections, I noticed some common heuristics and thinking-patterns. I built some of these into the solver (or at least tried to mimic them):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scan for the obvious – Some groups are very straightforward (the so-called “Yellow” easiest category). For instance, “Monday”, “Tuesday”, “Wednesday”, “Thursday” might appear together as days of the week. Word-game blogs call this out. &lt;/li&gt;
&lt;li&gt;Think about multi-meaning words / word-play – Some words in the grid may belong to multiple plausible groups, but the correct theme might hinge on a pun, a prefix, or a sound-alike. One Reddit comment:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Watch out for words that seem to belong to multiple categories!” &lt;br&gt;
My solver tries to penalise overly ambiguous candidates (e.g., words with many synsets).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Eliminate red herrings – The NYT puzzle makers often plant distractor words that look like they go in one category but don’t. For example, a word may appear to be a “color” but in fact is part of a phrase that links to something else. Recognising this helps. I introduced a heuristic: if a word easily fits several high-score groups, then maybe it’s a distractor.&lt;/li&gt;
&lt;li&gt;Solve 3 groups and the last one falls in place – Many players recognise that once 12 words are grouped, the remaining 4 must form the final category. This reduces search space. One comment remarks:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“I fall for the traps all the damn time lol”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building a NYT Connections Solver is a perfect small NLP project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s conceptually simple.&lt;/li&gt;
&lt;li&gt;It touches on embeddings, clustering, and visualization.&lt;/li&gt;
&lt;li&gt;It’s fun and rewarding to see AI recognize human-like patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project taught me a lot about how meaning can be quantified — and how sometimes, even machines can make surprising connections.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>Build a Smart Minecraft Auto-Farm Bot</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Tue, 09 Sep 2025 04:53:32 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/build-a-smart-minecraft-auto-farm-bot-1ooa</link>
      <guid>https://forem.com/leonardokaprio/build-a-smart-minecraft-auto-farm-bot-1ooa</guid>
      <description>&lt;p&gt;If you’re like me, you love the grind in Minecraft… until you don’t.&lt;/p&gt;

&lt;p&gt;Planting, harvesting, replanting—it’s satisfying at first, but once you’ve done it 40 times, it gets old. That’s when I thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Wait a second… I write bots for the web. Why not for Minecraft?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I did. With a bit of Node.js, a fantastic library called mineflayer&lt;br&gt;
, and some trial and error, I built a smart auto-farming bot that logs into my server, walks over to my farm, harvests crops, replants them, and chills until the next cycle.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You’ll Need:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Before diving in, here's what you'll need:&lt;/li&gt;
&lt;li&gt;A Minecraft Java Edition account&lt;/li&gt;
&lt;li&gt;A local or hosted Minecraft server (supporting bots)&lt;/li&gt;
&lt;li&gt;The mineflayer library&lt;/li&gt;
&lt;li&gt;A basic understanding of JavaScript and game loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 1: Set Up Your Project&lt;/p&gt;

&lt;p&gt;Start fresh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir mc-auto-farmer
cd mc-auto-farmer
npm init -y
npm install mineflayer vec3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create your entry file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch farmer.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Connect to the Minecraft Server
&lt;/h2&gt;

&lt;p&gt;Let’s start simple — login to &lt;a href="https://minecrraftapk.com/" rel="noopener noreferrer"&gt;Minecraft APK&lt;/a&gt; and spawn into the world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// farmer.js
const mineflayer = require('mineflayer');

const bot = mineflayer.createBot({
  host: 'localhost', // your server IP
  port: 25565,        // default Minecraft port
  username: 'FarmBot3000' // no auth needed for offline servers
});

bot.on('spawn', () =&amp;gt; {
  console.log('🧑‍🌾 Bot has spawned and is ready to farm!');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node farmer.js

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom. You're in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Walk to the Farm
&lt;/h2&gt;

&lt;p&gt;Now we need to guide the bot to the crop area. We’ll manually define the coordinates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { Vec3 } = require('vec3');

const farmLocation = new Vec3(100, 64, 100); // change to your farm's coords

bot.on('spawn', () =&amp;gt; {
  bot.chat('Heading to the farm!');
  bot.pathfinder.setGoal(new GoalBlock(farmLocation.x, farmLocation.y, farmLocation.z));
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait — we forgot something. pathfinder!&lt;/p&gt;

&lt;p&gt;Install and load it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install mineflayer-pathfinder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And require it properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { pathfinder, Movements, goals } = require('mineflayer-pathfinder');
const { GoalBlock } = goals;

bot.loadPlugin(pathfinder);

bot.on('spawn', () =&amp;gt; {
  const defaultMove = new Movements(bot);
  bot.pathfinder.setMovements(defaultMove);
  bot.pathfinder.setGoal(new GoalBlock(farmLocation.x, farmLocation.y, farmLocation.z));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your bot will navigate to the farm like a good little NPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Harvest + Replant Crops
&lt;/h2&gt;

&lt;p&gt;This is the fun part.&lt;/p&gt;

&lt;p&gt;We'll detect when the bot arrives, then scan for crops (like wheat) that are fully grown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot.on('goal_reached', async () =&amp;gt; {
  bot.chat('🌾 Arrived at the farm. Looking for ripe crops...');

  const crops = bot.findBlocks({
    matching: block =&amp;gt; block.name === 'wheat' &amp;amp;&amp;amp; block.metadata === 7,
    maxDistance: 6,
    count: 20,
  });

  for (const pos of crops) {
    const block = bot.blockAt(pos);

    if (block) {
      await bot.dig(block); // harvest
      await bot.placeBlock(bot.blockAt(pos.offset(0, -1, 0)), new Vec3(0, 1, 0)); // replant
    }
  }

  bot.chat('✅ Farming complete. Waiting for next cycle...');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re using metadata === 7 because that means the wheat is fully grown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Loop It with a Timer
&lt;/h2&gt;

&lt;p&gt;Let’s run this farming cycle every 5 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setInterval(() =&amp;gt; {
  bot.chat('⏳ Checking crops again...');
  bot.pathfinder.setGoal(new GoalBlock(farmLocation.x, farmLocation.y, farmLocation.z));
}, 5 * 60 * 1000);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional: Add random intervals to make it less bot-like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project was a blast to build. Not only did I automate one of Minecraft’s most repetitive tasks, but I also learned how accessible Minecraft automation is with JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to Take This Next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add chest storage logic (deposit harvested wheat)&lt;/li&gt;
&lt;li&gt;Support multiple crop types (carrots, potatoes, etc.)&lt;/li&gt;
&lt;li&gt;Build a Discord bot to report farming stats&lt;/li&gt;
&lt;li&gt;Use mineflayer’s inventory API to manage seeds/tools&lt;/li&gt;
&lt;li&gt;Detect hostile mobs and run away&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bonus Ideas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a farming network of multiple bots&lt;/li&gt;
&lt;li&gt;Connect your farm bot with a weather plugin (don’t farm in the rain)&lt;/li&gt;
&lt;li&gt;Auto-craft bread from harvested wheat&lt;/li&gt;
&lt;li&gt;Visualize farm productivity with a dashboard&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Track Null’s Brawl Battle Logs Using Node.js</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Wed, 27 Aug 2025 13:42:19 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/track-nulls-brawl-battle-logs-using-nodejs-4kl7</link>
      <guid>https://forem.com/leonardokaprio/track-nulls-brawl-battle-logs-using-nodejs-4kl7</guid>
      <description>&lt;p&gt;So, you’re deep into Null’s Brawl — enjoying unlimited gems, maxed-out brawlers, and all the skins you never had in the real game. But wouldn’t it be cool if you could track your battle logs, see how often you win, or even spot your most used brawlers in real-time?&lt;/p&gt;

&lt;p&gt;Let’s build something useful: a Battle Log Tracker Bot that monitors your match history in Null’s Brawl, stores it, and gives you insights about your playstyle. We’ll use Node.js, Express, a fake battle log API simulation (since Null’s Brawl is a private server), and lowdb for local JSON-based data storage.&lt;/p&gt;

&lt;p&gt;This is purely educational — for offline fun with Null's Brawl, not real Brawl Stars or for any competitive edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js installed&lt;/li&gt;
&lt;li&gt;Some knowledge of Express and async/await&lt;/li&gt;
&lt;li&gt;curl or Postman to simulate API calls&lt;/li&gt;
&lt;li&gt;A fake or emulated battle log API (we’ll mock this)&lt;/li&gt;
&lt;li&gt;A text editor (VS Code preferred)&lt;/li&gt;
&lt;li&gt;Coffee (because this is a long one!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Project Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir nulls-brawl-log-bot
cd nulls-brawl-log-bot
npm init -y
npm install express axios lowdb dotenv node-cron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch server.js api.js db.js .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In .env, store your player tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PLAYER_TAG=#Y0URFAKETAG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Mocking a Battle Log API
&lt;/h2&gt;

&lt;p&gt;Since &lt;a href="https://nulsbrawl.org/" rel="noopener noreferrer"&gt;Null’s Brawl&lt;/a&gt; doesn’t have a real public API, we’ll mock it for the sake of this bot.&lt;/p&gt;

&lt;p&gt;Create api.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api.js
const mockLog = [
  {
    battleTime: new Date().toISOString(),
    result: 'victory',
    brawler: 'Shelly',
    mode: 'Gem Grab'
  },
  {
    battleTime: new Date(Date.now() - 60000).toISOString(),
    result: 'defeat',
    brawler: 'Colt',
    mode: 'Showdown'
  }
];

async function getBattleLog(tag) {
  // Simulate an API call delay
  await new Promise((r) =&amp;gt; setTimeout(r, 500));
  return mockLog;
}

module.exports = { getBattleLog };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Store Battle Logs with LowDB
&lt;/h2&gt;

&lt;p&gt;In db.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { Low, JSONFile } = require('lowdb');
const path = require('path');

const file = path.join(__dirname, 'db.json');
const adapter = new JSONFile(file);
const db = new Low(adapter);

async function initDB() {
  await db.read();
  db.data ||= { logs: [] };
  await db.write();
}

async function saveBattleLog(entries) {
  await initDB();
  const existing = db.data.logs.map((e) =&amp;gt; e.battleTime);
  const newEntries = entries.filter((e) =&amp;gt; !existing.includes(e.battleTime));
  db.data.logs.push(...newEntries);
  await db.write();
}

async function getAllLogs() {
  await initDB();
  return db.data.logs;
}

module.exports = { saveBattleLog, getAllLogs };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Build the Express Server
&lt;/h2&gt;

&lt;p&gt;Now let’s glue it together in server.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('dotenv').config();
const express = require('express');
const cron = require('node-cron');
const { getBattleLog } = require('./api');
const { saveBattleLog, getAllLogs } = require('./db');

const app = express();
const port = 3000;

app.use(express.json());

// API route to fetch battle logs manually
app.get('/logs', async (req, res) =&amp;gt; {
  const logs = await getAllLogs();
  res.json(logs);
});

// Get most used brawler
app.get('/top-brawler', async (req, res) =&amp;gt; {
  const logs = await getAllLogs();
  const counts = {};

  logs.forEach(({ brawler }) =&amp;gt; {
    counts[brawler] = (counts[brawler] || 0) + 1;
  });

  const sorted = Object.entries(counts).sort((a, b) =&amp;gt; b[1] - a[1]);
  const [top, freq] = sorted[0] || ['None', 0];

  res.json({ topBrawler: top, timesUsed: freq });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Automate Battle Log Fetching
&lt;/h2&gt;

&lt;p&gt;Inside server.js, add a cron job that runs every 10 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cron.schedule('*/10 * * * *', async () =&amp;gt; {
  const logs = await getBattleLog(process.env.PLAYER_TAG);
  await saveBattleLog(logs);
  console.log(`[BOT] Synced ${logs.length} battle logs at ${new Date().toISOString()}`);
});

app.listen(port, () =&amp;gt; {
  console.log(`🎮 Null's Brawl Tracker Bot running at http://localhost:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Get Win Rate
&lt;/h2&gt;

&lt;p&gt;Let’s add one more API to calculate win rate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get('/winrate', async (req, res) =&amp;gt; {
  const logs = await getAllLogs();
  const total = logs.length;
  const wins = logs.filter(l =&amp;gt; l.result === 'victory').length;

  res.json({
    totalGames: total,
    wins,
    losses: total - wins,
    winRate: total ? ((wins / total) * 100).toFixed(2) + '%' : '0%'
  });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Everything
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start your bot:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Then hit the endpoints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/logs&lt;/code&gt; → See your synced battles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/top-brawler&lt;/code&gt; → Find your most used brawler&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/winrate&lt;/code&gt; → Win/loss ratio&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps: Go Bigger
&lt;/h2&gt;

&lt;p&gt;Once your local tracker works, here’s what you could build next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dashboard with Chart.js or React&lt;/li&gt;
&lt;li&gt;Store logs in a proper DB (like MongoDB)&lt;/li&gt;
&lt;li&gt;Analyze mode-based performance (e.g., Gem Grab vs Showdown)&lt;/li&gt;
&lt;li&gt;Add a CLI tool to run stats in terminal&lt;/li&gt;
&lt;li&gt;Discord integration to post win rates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Null’s Brawl might not be official, but that doesn’t mean you can’t build real tools around it. With a little Node.js, some smart data tracking, and time, you can create your own analytics platform — even without an official API.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>java</category>
    </item>
    <item>
      <title>how to build a youtube clone with python</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Sun, 24 Aug 2025 15:35:15 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/how-to-build-a-youtube-clone-with-python-3n30</link>
      <guid>https://forem.com/leonardokaprio/how-to-build-a-youtube-clone-with-python-3n30</guid>
      <description>&lt;p&gt;YouTube has transformed the way people consume video content, making it a giant in the digital entertainment industry. But what if you wanted to create your own mini-YouTube platform where users can upload, stream, and manage videos? Thanks to Python and its powerful frameworks like Flask and Django, this is entirely possible. In this article, we will walk through building a basic YouTube clone using Python with Flask. While it won’t match the scale and features of YouTube, this project will give you a strong understanding of how video streaming platforms work under the hood. &lt;/p&gt;

&lt;h2&gt;
  
  
  1) Quick setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows
# .venv\Scripts\activate

pip install flask sqlalchemy flask-login werkzeug
mkdir uploads instance

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;app.py&lt;/code&gt; in an empty folder. We’ll add code progressively.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Minimal Flask app + database models
&lt;/h2&gt;

&lt;p&gt;We’ll define Users, Videos, Comments, and Likes. SQLite keeps things simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.py
import os
from datetime import datetime
from typing import Optional

from flask import Flask, request, redirect, url_for, render_template_string, abort, send_file, Response, flash, jsonify
from flask_login import LoginManager, UserMixin, login_user, logout_user, current_user, login_required
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename

from sqlalchemy import create_engine, select, func, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session

BASE_DIR = os.path.abspath(os.path.dirname(__file__))
UPLOAD_DIR = os.path.join(BASE_DIR, "uploads")
os.makedirs(UPLOAD_DIR, exist_ok=True)

app = Flask(__name__, instance_relative_config=True)
app.config["SECRET_KEY"] = "dev-secret-change-me"
os.makedirs(app.instance_path, exist_ok=True)

# DB
engine = create_engine(f"sqlite:///{os.path.join(app.instance_path, 'app.db')}", echo=False)

class Base(DeclarativeBase): pass

class User(UserMixin, Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    username: Mapped[str] = mapped_column(unique=True, index=True)
    password_hash: Mapped[str]
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    videos: Mapped[list["Video"]] = relationship(back_populates="owner")
    def set_password(self, pw): self.password_hash = generate_password_hash(pw)
    def check_password(self, pw): return check_password_hash(self.password_hash, pw)

class Video(Base):
    __tablename__ = "videos"
    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(index=True)
    description: Mapped[str] = mapped_column(default="")
    filename: Mapped[str]
    mimetype: Mapped[str]
    views: Mapped[int] = mapped_column(default=0)
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    owner_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    owner: Mapped[User] = relationship(back_populates="videos")
    comments: Mapped[list["Comment"]] = relationship(back_populates="video", cascade="all, delete-orphan")
    likes: Mapped[list["Like"]] = relationship(back_populates="video", cascade="all, delete-orphan")

class Comment(Base):
    __tablename__ = "comments"
    id: Mapped[int] = mapped_column(primary_key=True)
    body: Mapped[str]
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    video_id: Mapped[int] = mapped_column(ForeignKey("videos.id"))
    video: Mapped[Video] = relationship(back_populates="comments")

class Like(Base):
    __tablename__ = "likes"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    video_id: Mapped[int] = mapped_column(ForeignKey("videos.id"))
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    video: Mapped[Video] = relationship(back_populates="likes")

Base.metadata.create_all(engine)

login_manager = LoginManager(app)
login_manager.login_view = "login"

@login_manager.user_loader
def load_user(uid: str) -&amp;gt; Optional[User]:
    with Session(engine) as s:
        return s.get(User, int(uid))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3) Tiniest UI (embedded templates) and homepage
&lt;/h2&gt;

&lt;p&gt;We’ll embed a minimal template for speed. Later you can split to real files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TPL_BASE = """
&amp;lt;!doctype html&amp;gt;&amp;lt;meta charset="utf-8"&amp;gt;&amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
&amp;lt;title&amp;gt;{{ title or 'PyTube' }}&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
body{margin:0;font-family:system-ui,Segoe UI,Roboto,Arial;background:#0b0e12;color:#e6edf3}
.container{max-width:1000px;margin:0 auto;padding:20px}
.nav{display:flex;gap:12px;align-items:center;justify-content:space-between}
.search{flex:1;margin:0 12px}
.search input{width:100%;padding:10px;border-radius:10px;border:1px solid #253041;background:#0f141b;color:#e6edf3}
.btn{background:#22c55e;border:none;padding:10px 14px;border-radius:10px;color:#052e12;font-weight:700;cursor:pointer}
.btn.outline{background:transparent;border:1px solid #2b3543;color:#e6edf3}
.card{background:#11161d;border:1px solid #1b2430;border-radius:16px;padding:12px;margin:12px 0}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}
.thumb{aspect-ratio:16/9;background:#0f141b;border-radius:10px;display:flex;align-items:center;justify-content:center;border:1px dashed #2b3543}
.meta{font-size:12px;color:#a7b1c0}
.video{width:100%;background:#000;border-radius:12px;border:1px solid #1b2430}
.flash{padding:10px;border:1px solid #273141;border-radius:10px;background:#0f141b;margin:8px 0}
&amp;lt;/style&amp;gt;
&amp;lt;div class="container"&amp;gt;
  &amp;lt;div class="nav"&amp;gt;
    &amp;lt;div style="display:flex;gap:12px;align-items:center"&amp;gt;
      &amp;lt;a href="/" style="font-weight:800;text-decoration:none;color:#e6edf3"&amp;gt;PyTube&amp;lt;/a&amp;gt;
      &amp;lt;form class="search" method="get" action="{{ url_for('index') }}"&amp;gt;
        &amp;lt;input name="q" placeholder="Search videos..." value="{{ q or '' }}"&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div style="display:flex;gap:8px"&amp;gt;
      {% if current_user.is_authenticated %}
        &amp;lt;a class="btn outline" href="{{ url_for('upload') }}"&amp;gt;Upload&amp;lt;/a&amp;gt;
        &amp;lt;a class="btn outline" href="{{ url_for('logout') }}"&amp;gt;Logout&amp;lt;/a&amp;gt;
      {% else %}
        &amp;lt;a class="btn outline" href="{{ url_for('login') }}"&amp;gt;Log in&amp;lt;/a&amp;gt;
        &amp;lt;a class="btn" href="{{ url_for('register') }}"&amp;gt;Sign up&amp;lt;/a&amp;gt;
      {% endif %}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  {% for c, m in get_flashed_messages(with_categories=true) %}&amp;lt;div class="flash"&amp;gt;{{ m }}&amp;lt;/div&amp;gt;{% endfor %}
  {% block content %}{% endblock %}
&amp;lt;/div&amp;gt;
"""

TPL_INDEX = """
{% extends TPL_BASE %}
{% block content %}
&amp;lt;h2&amp;gt;Latest uploads{% if q %} matching “{{ q }}”{% endif %}&amp;lt;/h2&amp;gt;
{% if videos %}
&amp;lt;div class="grid"&amp;gt;
{% for v in videos %}
  &amp;lt;a class="card" href="{{ url_for('watch', video_id=v.id) }}" style="text-decoration:none;color:#e6edf3"&amp;gt;
    &amp;lt;div class="thumb"&amp;gt;▶&amp;lt;/div&amp;gt;
    &amp;lt;div style="font-weight:700"&amp;gt;{{ v.title }}&amp;lt;/div&amp;gt;
    &amp;lt;div class="meta"&amp;gt;{{ v.views }} views • {{ v.created_at.strftime('%b %d, %Y') }}&amp;lt;/div&amp;gt;
  &amp;lt;/a&amp;gt;
{% endfor %}
&amp;lt;/div&amp;gt;
{% else %}
  &amp;lt;div class="card"&amp;gt;&amp;lt;div class="meta"&amp;gt;No videos yet. Be the first to upload!&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
{% endif %}
{% endblock %}
"""

app.jinja_env.globals["TPL_BASE"] = TPL_BASE

@app.route("/")
def index():
    q = request.args.get("q", "").strip()
    with Session(engine) as s:
        stmt = select(Video).order_by(Video.created_at.desc())
        if q:
            stmt = select(Video).where(Video.title.ilike(f"%{q}%")).order_by(Video.created_at.desc())
        videos = s.scalars(stmt).all()
    return render_template_string(TPL_INDEX, videos=videos, q=q)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4) Authentication (register, login, logout)
&lt;/h2&gt;

&lt;p&gt;A clean, minimal form flow using &lt;code&gt;flask-login&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TPL_AUTH = """
{% extends TPL_BASE %}
{% block content %}
&amp;lt;h2&amp;gt;{{ 'Create an account' if mode=='register' else 'Log in' }}&amp;lt;/h2&amp;gt;
&amp;lt;form method="post" class="card"&amp;gt;
  &amp;lt;label&amp;gt;Username&amp;lt;/label&amp;gt;&amp;lt;input name="username"&amp;gt;
  &amp;lt;label&amp;gt;Password&amp;lt;/label&amp;gt;&amp;lt;input type="password" name="password"&amp;gt;
  &amp;lt;button class="btn" type="submit"&amp;gt;{{ 'Sign up' if mode=='register' else 'Log in' }}&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
{% endblock %}
"""

@app.route("/register", methods=["GET","POST"])
def register():
    if request.method == "POST":
        u = request.form.get("username","").strip()
        p = request.form.get("password","")
        if len(u) &amp;lt; 3 or len(p) &amp;lt; 6:
            flash("Username or password too short.", "error")
        else:
            with Session(engine) as s:
                exists = s.scalar(select(func.count(User.id)).where(User.username==u)) &amp;gt; 0
                if exists:
                    flash("Username already taken.", "error")
                else:
                    user = User(username=u); user.set_password(p)
                    s.add(user); s.commit()
                    flash("Account created. Please log in.", "success")
                    return redirect(url_for("login"))
    return render_template_string(TPL_AUTH, mode="register")

@app.route("/login", methods=["GET","POST"])
def login():
    if request.method == "POST":
        u = request.form.get("username","").strip()
        p = request.form.get("password","")
        with Session(engine) as s:
            user = s.scalar(select(User).where(User.username==u))
            if user and user.check_password(p):
                login_user(user); flash("Welcome back!", "success")
                return redirect(url_for("index"))
        flash("Invalid credentials.", "error")
    return render_template_string(TPL_AUTH, mode="login")

@app.route("/logout")
@login_required
def logout():
    logout_user(); flash("Logged out.", "success")
    return redirect(url_for("index"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5) Upload videos
&lt;/h2&gt;

&lt;p&gt;We’ll accept &lt;code&gt;.mp4,&lt;/code&gt; .&lt;code&gt;webm,&lt;/code&gt; &lt;code&gt;.ogg,&lt;/code&gt; .&lt;code&gt;m4v.&lt;/code&gt; Filenames are sanitized and de-duplicated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALLOWED_EXTS = {".mp4", ".webm", ".ogg", ".m4v"}
def allowed(name): return os.path.splitext(name.lower())[1] in ALLOWED_EXTS

TPL_UPLOAD = """
{% extends TPL_BASE %}
{% block content %}
&amp;lt;h2&amp;gt;Upload a video&amp;lt;/h2&amp;gt;
&amp;lt;form method="post" enctype="multipart/form-data" class="card"&amp;gt;
  &amp;lt;label&amp;gt;Title&amp;lt;/label&amp;gt;&amp;lt;input name="title"&amp;gt;
  &amp;lt;label&amp;gt;Description&amp;lt;/label&amp;gt;&amp;lt;textarea name="description" rows="3"&amp;gt;&amp;lt;/textarea&amp;gt;
  &amp;lt;label&amp;gt;File&amp;lt;/label&amp;gt;&amp;lt;input type="file" name="file" accept="video/*,.mp4,.webm,.ogg,.m4v"&amp;gt;
  &amp;lt;button class="btn" type="submit"&amp;gt;Upload&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
{% endblock %}
"""

@app.route("/upload", methods=["GET","POST"])
@login_required
def upload():
    if request.method == "POST":
        title = request.form.get("title","").strip()
        desc = request.form.get("description","").strip()
        f = request.files.get("file")
        if not title or not f or not f.filename:
            flash("Title and file are required.", "error")
        elif not allowed(f.filename):
            flash("Unsupported file type.", "error")
        else:
            name = secure_filename(f.filename)
            base, ext = os.path.splitext(name)
            dest = os.path.join(UPLOAD_DIR, name)
            i = 1
            while os.path.exists(dest):
                name = f"{base}_{i}{ext}"; dest = os.path.join(UPLOAD_DIR, name); i += 1
            f.save(dest)
            with Session(engine) as s:
                v = Video(title=title, description=desc, filename=name, mimetype=f.mimetype or "video/mp4",
                          owner_id=current_user.id)
                s.add(v); s.commit()
                flash("Uploaded!", "success")
                return redirect(url_for("watch", video_id=v.id))
    return render_template_string(TPL_UPLOAD)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6) Range-aware streaming endpoint
&lt;/h2&gt;

&lt;p&gt;This is the secret sauce. It supports scrubbing and fast starts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def partial_response(path, mimetype):
    size = os.path.getsize(path)
    rng = request.headers.get("Range")
    if rng:
        # e.g. "bytes=1000-2000"
        units, _, spec = rng.partition("=")
        if units != "bytes": abort(416)
        start_s, _, end_s = spec.partition("-")
        try:
            start = int(start_s) if start_s else 0
            end = int(end_s) if end_s else size - 1
        except ValueError:
            abort(416)
        start = max(0, start); end = min(size - 1, end)
        length = end - start + 1
        with open(path, "rb") as fp:
            fp.seek(start)
            data = fp.read(length)
        resp = Response(data, 206, mimetype=mimetype, direct_passthrough=True)
        resp.headers.add("Content-Range", f"bytes {start}-{end}/{size}")
        resp.headers.add("Accept-Ranges", "bytes")
        resp.headers.add("Content-Length", str(length))
        return resp
    return send_file(path, mimetype=mimetype, as_attachment=False)

@app.route("/stream/&amp;lt;int:video_id&amp;gt;")
def stream(video_id: int):
    with Session(engine) as s:
        v = s.get(Video, video_id) or abort(404)
    path = os.path.join(UPLOAD_DIR, v.filename)
    if not os.path.exists(path): abort(404)
    return partial_response(path, v.mimetype)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7) Watch page (player + views counter)
&lt;/h2&gt;

&lt;p&gt;We render a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; player that points to our Range endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TPL_WATCH = """
{% extends TPL_BASE %}
{% block content %}
&amp;lt;div class="card"&amp;gt;
  &amp;lt;video class="video" controls preload="metadata"&amp;gt;
    &amp;lt;source src="{{ url_for('stream', video_id=v.id) }}" type="{{ v.mimetype }}"&amp;gt;
    Your browser does not support the video tag.
  &amp;lt;/video&amp;gt;
  &amp;lt;h2 style="margin:6px 0 0"&amp;gt;{{ v.title }}&amp;lt;/h2&amp;gt;
  &amp;lt;div class="meta"&amp;gt;{{ v.views }} views • {{ v.created_at.strftime('%b %d, %Y') }}&amp;lt;/div&amp;gt;
  {% if v.description %}&amp;lt;p style="margin-top:8px"&amp;gt;{{ v.description }}&amp;lt;/p&amp;gt;{% endif %}
&amp;lt;/div&amp;gt;
{% endblock %}
"""

@app.route("/v/&amp;lt;int:video_id&amp;gt;")
def watch(video_id: int):
    with Session(engine) as s:
        v = s.get(Video, video_id) or abort(404)
        v.views += 1; s.commit()
    return render_template_string(TPL_WATCH, v=v)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also if you want to download youtube videos then must download &lt;a href="https://snaptubeag.com/" rel="noopener noreferrer"&gt;snaptube apk&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8) Likes (AJAX) and comments (form)
&lt;/h2&gt;

&lt;p&gt;Small, friendly community features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Like toggle: returns JSON {count: &amp;lt;int&amp;gt;}
@app.route("/like/&amp;lt;int:video_id&amp;gt;", methods=["POST"])
@login_required
def like(video_id: int):
    with Session(engine) as s:
        v = s.get(Video, video_id) or abort(404)
        existing = s.scalar(select(Like).where(Like.video_id==video_id, Like.user_id==current_user.id))
        if existing:
            s.delete(existing); s.commit()
        else:
            s.add(Like(video_id=video_id, user_id=current_user.id)); s.commit()
        count = s.scalar(select(func.count(Like.id)).where(Like.video_id==video_id)) or 0
    return jsonify({"count": count})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the watch template to show a like button and a comment box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TPL_WATCH = """
{% extends TPL_BASE %}
{% block content %}
&amp;lt;div class="card"&amp;gt;
  &amp;lt;video class="video" controls preload="metadata"&amp;gt;
    &amp;lt;source src="{{ url_for('stream', video_id=v.id) }}" type="{{ v.mimetype }}"&amp;gt;
  &amp;lt;/video&amp;gt;
  &amp;lt;h2 style="margin:6px 0 0"&amp;gt;{{ v.title }}&amp;lt;/h2&amp;gt;
  &amp;lt;div class="meta"&amp;gt;{{ v.views }} views • {{ v.created_at.strftime('%b %d, %Y') }}&amp;lt;/div&amp;gt;
  &amp;lt;div style="display:flex;gap:10px;align-items:center;margin-top:8px"&amp;gt;
    &amp;lt;button class="btn" onclick="likeVideo({{ v.id }})"&amp;gt;♥ Like&amp;lt;/button&amp;gt;
    &amp;lt;div class="meta"&amp;gt;Likes: &amp;lt;span id="likeCount"&amp;gt;{{ like_count }}&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  {% if v.description %}&amp;lt;p style="margin-top:8px"&amp;gt;{{ v.description }}&amp;lt;/p&amp;gt;{% endif %}
&amp;lt;/div&amp;gt;

&amp;lt;div class="card"&amp;gt;
  &amp;lt;h3&amp;gt;Comments&amp;lt;/h3&amp;gt;
  {% if current_user.is_authenticated %}
    &amp;lt;form method="post" action="{{ url_for('comment', video_id=v.id) }}"&amp;gt;
      &amp;lt;input name="body" placeholder="Say something nice…" style="width:100%;margin-bottom:8px"&amp;gt;
      &amp;lt;button class="btn" type="submit"&amp;gt;Post&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  {% else %}
    &amp;lt;div class="meta"&amp;gt;Log in to comment.&amp;lt;/div&amp;gt;
  {% endif %}
  {% for c in comments %}
    &amp;lt;div style="padding:10px;border:1px solid #273141;border-radius:10px;background:#0f141b;margin-top:8px"&amp;gt;
      &amp;lt;div class="meta"&amp;gt;{{ c.created_at.strftime('%b %d, %Y') }}&amp;lt;/div&amp;gt;
      &amp;lt;div style="margin-top:4px"&amp;gt;{{ c.body }}&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  {% else %}
    &amp;lt;div class="meta"&amp;gt;No comments yet.&amp;lt;/div&amp;gt;
  {% endfor %}
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
async function likeVideo(id){
  const res = await fetch(`/like/${id}`, {method:'POST'});
  const data = await res.json();
  const el = document.querySelector('#likeCount');
  if(el){ el.textContent = data.count; }
}
&amp;lt;/script&amp;gt;
{% endblock %}
"""

@app.route("/v/&amp;lt;int:video_id&amp;gt;")
def watch(video_id: int):
    with Session(engine) as s:
        v = s.get(Video, video_id) or abort(404)
        v.views += 1; s.commit()
        comments = s.scalars(select(Comment).where(Comment.video_id==v.id).order_by(Comment.created_at.desc())).all()
        like_count = s.scalar(select(func.count(Like.id)).where(Like.video_id==v.id)) or 0
    return render_template_string(TPL_WATCH, v=v, comments=comments, like_count=like_count)

@app.route("/comment/&amp;lt;int:video_id&amp;gt;", methods=["POST"])
@login_required
def comment(video_id: int):
    body = request.form.get("body","").strip()
    if not body: flash("Comment cannot be empty.", "error")
    else:
        with Session(engine) as s:
            v = s.get(Video, video_id) or abort(404)
            s.add(Comment(body=body, user_id=current_user.id, video_id=video_id)); s.commit()
            flash("Comment posted!", "success")
    return redirect(url_for("watch", video_id=video_id))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9) Search on the homepage
&lt;/h2&gt;

&lt;p&gt;We already wired a query param &lt;code&gt;q&lt;/code&gt; in Step 2. Try it: type a word and hit Enter. It filters by title using case-insensitive &lt;code&gt;ilike&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10) Run the app
&lt;/h2&gt;

&lt;p&gt;python app.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Open http://127.0.0.1:5000
# 1) Register  2) Log in  3) Upload a small .mp4  4) Click the video
# Drag the scrubber — Range streaming should work smoothly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final notes
&lt;/h3&gt;

&lt;p&gt;A “YouTube clone” is just well-known web primitives—uploads, byte ranges, a few tables—combined thoughtfully. Start with this skeleton, confirm streaming works, then iterate toward thumbnails, HLS, and scale.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Automate Your Spotify Playlists Using Node.js</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Sat, 23 Aug 2025 16:54:24 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/automate-your-spotify-playlists-using-nodejs-186h</link>
      <guid>https://forem.com/leonardokaprio/automate-your-spotify-playlists-using-nodejs-186h</guid>
      <description>&lt;p&gt;If you're tired of hitting "Repeat" on the same songs or spending hours tweaking playlists, this project is for you.&lt;/p&gt;

&lt;p&gt;Imagine a Spotify bot that listens with you — detecting your preferences, learning your moods, and generating personalized playlists that evolve as your music taste shifts. Thanks to the power of Node.js and the Spotify Web API, this isn't just possible — it's easier than you might think.&lt;/p&gt;

&lt;p&gt;In this article, we’ll build a Smart Spotify Bot that uses real audio features and behavior analysis to automatically generate custom playlists. Whether you want upbeat tracks for workouts or mellow vibes for coding, this bot has you covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools You’ll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Spotify Developer Account&lt;/li&gt;
&lt;li&gt;A registered Spotify app (Client ID, Client Secret)&lt;/li&gt;
&lt;li&gt;Node.js &amp;amp; npm&lt;/li&gt;
&lt;li&gt;REST &amp;amp; OAuth 2.0 basics&lt;/li&gt;
&lt;li&gt;A code editor like VS Code&lt;/li&gt;
&lt;li&gt;Your favorite music taste&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;Here’s what our bot will do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate the user (OAuth 2.0)&lt;/li&gt;
&lt;li&gt;Pull top tracks from Spotify&lt;/li&gt;
&lt;li&gt;Analyze them using Spotify’s audio features (like danceability, tempo, etc.)&lt;/li&gt;
&lt;li&gt;Apply smart filters to find tracks that match your desired mood&lt;/li&gt;
&lt;li&gt;Create a playlist and fill it with those tracks&lt;/li&gt;
&lt;li&gt;Optionally, automate it on a schedule or extend it with genre/mood filters&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up Spotify App &amp;amp; Environment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Spotify App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to the Spotify Developer Dashboard&lt;br&gt;
, create an app, and set your Redirect URI (e.g., &lt;code&gt;http://localhost:8888/callback&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Initialize Node Project&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir smart-spotify-bot &amp;amp;&amp;amp; cd smart-spotify-bot
npm init -y
npm install express axios dotenv querystring open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Create .env&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REDIRECT_URI=http://localhost:8888/callback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Build the OAuth Flow with Express
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.js
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const querystring = require('querystring');
const open = require('open');

const app = express();
const PORT = 8888;

const SCOPES = [
  'user-read-private',
  'playlist-modify-public',
  'user-top-read'
].join(' ');

app.get('/login', (req, res) =&amp;gt; {
  const params = querystring.stringify({
    response_type: 'code',
    client_id: process.env.SPOTIFY_CLIENT_ID,
    scope: SCOPES,
    redirect_uri: process.env.SPOTIFY_REDIRECT_URI,
  });
  res.redirect(`https://accounts.spotify.com/authorize?${params}`);
});

app.get('/callback', async (req, res) =&amp;gt; {
  const code = req.query.code;

  try {
    const tokenRes = await axios.post('https://accounts.spotify.com/api/token',
      querystring.stringify({
        code,
        redirect_uri: process.env.SPOTIFY_REDIRECT_URI,
        grant_type: 'authorization_code',
      }),
      {
        headers: {
          Authorization: 'Basic ' + Buffer.from(
            `${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`
          ).toString('base64'),
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }
    );

    const { access_token, refresh_token } = tokenRes.data;
    console.log('Access Token:', access_token);
    console.log('Refresh Token:', refresh_token);

    res.send('✅ Auth complete. Check your console.');
  } catch (err) {
    console.error('Error:', err.response.data);
    res.send('❌ Failed to authenticate.');
  }
});

app.listen(PORT, () =&amp;gt; {
  console.log(`🔐 Go to http://localhost:${PORT}/login`);
  open(`http://localhost:${PORT}/login`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node auth.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Fetch and Filter Top Tracks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Get Top Tracks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getTopTracks(token) {
  const res = await axios.get('https://api.spotify.com/v1/me/top/tracks?limit=20', {
    headers: { Authorization: `Bearer ${token}` },
  });
  return res.data.items;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get Audio Features&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getAudioFeatures(token, trackIds) {
  const res = await axios.get(`https://api.spotify.com/v1/audio-features?ids=${trackIds.join(',')}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  return res.data.audio_features;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Filter by Mood and Create Playlist
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterTracksByMood(tracks, features) {
  return tracks.filter((_, i) =&amp;gt; {
    const f = features[i];
    return f.energy &amp;gt; 0.65 &amp;amp;&amp;amp; f.valence &amp;gt; 0.5 &amp;amp;&amp;amp; f.danceability &amp;gt; 0.6;
  });
}

async function createPlaylist(token, userId, name) {
  const res = await axios.post(`https://api.spotify.com/v1/users/${userId}/playlists`, {
    name,
    description: 'Auto-generated by Smart Spotify Bot',
    public: false
  }, {
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });
  return res.data.id;
}

async function addTracksToPlaylist(token, playlistId, uris) {
  await axios.post(`https://api.spotify.com/v1/playlists/${playlistId}/tracks`, {
    uris
  }, {
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Run the Bot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function runSmartBot(token) {
  const topTracks = await getTopTracks(token);
  const trackIds = topTracks.map(t =&amp;gt; t.id);
  const features = await getAudioFeatures(token, trackIds);

  const filtered = filterTracksByMood(topTracks, features);
  const uris = filtered.map(t =&amp;gt; t.uri);

  const me = await axios.get('https://api.spotify.com/v1/me', {
    headers: { Authorization: `Bearer ${token}` }
  });
  const userId = me.data.id;

  const playlistId = await createPlaylist(token, userId, '🎶 Auto Vibes');
  await addTracksToPlaylist(token, playlistId, uris);

  console.log(`✅ Created playlist with ${uris.length} tracks`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Expand the Bot
&lt;/h2&gt;

&lt;p&gt;You’ve got a working &lt;a href="https://spotipremiums.com/" rel="noopener noreferrer"&gt;APK Spotify Premium&lt;/a&gt; playlist generator — but this is just the beginning. Here are some ideas to take it further:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schedule It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use node-cron to run the bot weekly or daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Genre-Based Filters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Filter by genres like chill, rock, or EDM using track metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mood Detection by Time/Weather&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use APIs (like OpenWeather) to change playlist themes based on real-time data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save Listening Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Store user track data in a database to analyze trends and evolve the playlist algorithm over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Spotify’s API is an amazing playground for creative automation. With a few lines of JavaScript and a bit of logic, you can build a personal music assistant that adapts to you — not the other way around.&lt;/p&gt;

&lt;p&gt;Start small, build something fun, and before you know it, you’ll have a smart music ecosystem that plays what you love, when you love it.&lt;/p&gt;

&lt;p&gt;Ready to build your own music butler? 🎶&lt;br&gt;
Go fork this into your next project and let your code curate your vibe.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Create a Smart Spotify Bot with Node.js &amp; Web API</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Fri, 15 Aug 2025 04:23:03 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/create-a-smart-spotify-bot-with-nodejs-web-api-9oo</link>
      <guid>https://forem.com/leonardokaprio/create-a-smart-spotify-bot-with-nodejs-web-api-9oo</guid>
      <description>&lt;p&gt;If you’re like me, your playlists are your digital mixtapes – carefully curated soundtracks for workouts, chill sessions, work grinds, or late-night drives. But building and maintaining them manually? That can be a drag.&lt;/p&gt;

&lt;h2&gt;
  
  
  That’s when I had this idea:
&lt;/h2&gt;

&lt;p&gt;What if I could write a bot that builds playlists based on my listening habits, audio features, and moods – automatically?&lt;/p&gt;

&lt;p&gt;With the power of the Spotify Web API, a little bit of Node.js, and some creative logic, I built a small utility bot that does just that. In this post, I’ll walk you through how you can build your own Spotify Playlist Curator Bot that creates smart playlists based on your taste.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll Need
&lt;/h2&gt;

&lt;p&gt;Before we dive into code, let’s list the requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Spotify Developer Account&lt;/li&gt;
&lt;li&gt;A Spotify App with its Client ID and Client Secret&lt;/li&gt;
&lt;li&gt;A Node.js project setup&lt;/li&gt;
&lt;li&gt;Some basic familiarity with JavaScript, OAuth2, and REST APIs&lt;/li&gt;
&lt;li&gt;A text editor (I used VS Code)&lt;/li&gt;
&lt;li&gt;And of course, your music taste&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Authenticate with Spotify (OAuth 2.0 Authorization Code Flow)
&lt;/h2&gt;

&lt;p&gt;Spotify requires OAuth2 to access user data, so the first thing we need is to authenticate the user and obtain an access token.&lt;/p&gt;

&lt;p&gt;Here’s a quick setup using the express framework and axios to handle HTTP requests:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup your environment:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
npm install express axios dotenv querystring open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a .env file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPOTIFY_CLIENT_ID=your_client_id_here
SPOTIFY_CLIENT_SECRET=your_client_secret_here
SPOTIFY_REDIRECT_URI=http://localhost:8888/callback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a basic Express server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server.js
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const querystring = require('querystring');
const open = require('open');

const app = express();
const port = 8888;

const SCOPES = [
  'user-read-private',
  'user-read-email',
  'playlist-modify-public',
  'playlist-modify-private',
  'user-top-read',
].join(' ');

app.get('/login', (req, res) =&amp;gt; {
  const queryParams = querystring.stringify({
    response_type: 'code',
    client_id: process.env.SPOTIFY_CLIENT_ID,
    scope: SCOPES,
    redirect_uri: process.env.SPOTIFY_REDIRECT_URI,
  });

  res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`);
});

app.get('/callback', async (req, res) =&amp;gt; {
  const code = req.query.code;

  try {
    const response = await axios.post(
      'https://accounts.spotify.com/api/token',
      querystring.stringify({
        grant_type: 'authorization_code',
        code,
        redirect_uri: process.env.SPOTIFY_REDIRECT_URI,
      }),
      {
        headers: {
          Authorization:
            'Basic ' +
            Buffer.from(
              `${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`
            ).toString('base64'),
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }
    );

    const { access_token, refresh_token } = response.data;

    res.send('✅ Authenticated. Now you can close this tab and run your bot.');
    console.log('Access Token:', access_token);
    console.log('Refresh Token:', refresh_token);

    // Store these tokens securely or pass to your bot logic
  } catch (err) {
    console.error('Error fetching access token:', err.response.data);
    res.send('Authentication failed.');
  }
});

app.listen(port, () =&amp;gt; {
  console.log(`&amp;gt; Visit http://localhost:${port}/login to authenticate`);
  open(`http://localhost:${port}/login`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run this with:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once authenticated, you'll get your tokens in the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Get User's Top Tracks
&lt;/h2&gt;

&lt;p&gt;Spotify allows access to the user's most played tracks over different time ranges. Let's use that to seed our playlist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getTopTracks(token, time_range = 'short_term') {
  const response = await axios.get(
    `https://api.spotify.com/v1/me/top/tracks?limit=20&amp;amp;time_range=${time_range}`,
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    }
  );

  return response.data.items;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can choose time ranges like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;short_term&lt;/code&gt; (last 4 weeks)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;medium_term&lt;/code&gt; (last 6 months)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;long_term&lt;/code&gt; (all time)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Analyze Tracks &amp;amp; Create Playlist
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://spotipremiiumapk.com/" rel="noopener noreferrer"&gt;Spotify Premium APK&lt;/a&gt; offers audio features for every track — things like danceability, energy, tempo, and valence (positivity).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getAudioFeatures(token, trackIds) {
  const ids = trackIds.join(',');
  const response = await axios.get(
    `https://api.spotify.com/v1/audio-features?ids=${ids}`,
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    }
  );

  return response.data.audio_features;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Based on this, let’s filter tracks with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;danceability &amp;gt; 0.7&lt;/li&gt;
&lt;li&gt;energy &amp;gt; 0.6&lt;/li&gt;
&lt;li&gt;valence &amp;gt; 0.5&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can adjust this for your mood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now Create the Playlist:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function createPlaylist(token, userId, name, description) {
  const response = await axios.post(
    `https://api.spotify.com/v1/users/${userId}/playlists`,
    {
      name,
      description,
      public: false,
    },
    {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
    }
  );

  return response.data.id;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And finally, add songs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function addTracksToPlaylist(token, playlistId, uris) {
  await axios.post(
    `https://api.spotify.com/v1/playlists/${playlistId}/tracks`,
    {
      uris,
    },
    {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
    }
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Here’s the logic of the final bot:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function runBot(accessToken) {
  const topTracks = await getTopTracks(accessToken, 'short_term');
  const trackIds = topTracks.map(track =&amp;gt; track.id);

  const features = await getAudioFeatures(accessToken, trackIds);

  const filteredTracks = topTracks.filter((track, i) =&amp;gt; {
    const f = features[i];
    return f &amp;amp;&amp;amp; f.danceability &amp;gt; 0.7 &amp;amp;&amp;amp; f.energy &amp;gt; 0.6 &amp;amp;&amp;amp; f.valence &amp;gt; 0.5;
  });

  const uris = filteredTracks.map(track =&amp;gt; track.uri);

  const me = await axios.get('https://api.spotify.com/v1/me', {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  const userId = me.data.id;

  const playlistId = await createPlaylist(
    accessToken,
    userId,
    '💃 Curated Mood Booster',
    'Auto-curated feel-good tracks based on your recent listening habits.'
  );

  await addTracksToPlaylist(accessToken, playlistId, uris);

  console.log(`✅ Playlist created with ${uris.length} tracks!`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project was a blast.&lt;/p&gt;

&lt;p&gt;What I love about this is how easy it is to build something meaningful with tools like Node.js and the Spotify Web API. With a few API calls and some logic, you’ve got a personal DJ assistant that creates vibe-based playlists for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can take this further:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schedule the bot to run weekly with a cron job&lt;/li&gt;
&lt;li&gt;Email yourself a summary of your top tracks&lt;/li&gt;
&lt;li&gt;Analyze genres or artists&lt;/li&gt;
&lt;li&gt;Build mood-based playlists for different times of day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧪 Bonus: Ideas to Expand&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a web dashboard using React or Vue&lt;/li&gt;
&lt;li&gt;Store history of generated playlists&lt;/li&gt;
&lt;li&gt;Use AI/ML to predict mood from listening patterns&lt;/li&gt;
&lt;li&gt;Integrate with Discord to share playlists&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Connect Spotify to Discord</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Mon, 04 Aug 2025 10:34:15 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/how-to-connect-spotify-to-discord-3406</link>
      <guid>https://forem.com/leonardokaprio/how-to-connect-spotify-to-discord-3406</guid>
      <description>&lt;p&gt;If you’re someone who loves music and spends a lot of time chatting with friends on Discord, connecting your Spotify account can take your experience to the next level. Whether you're streaming your favorite playlist during a gaming session or just want to show off what you're listening to, integrating Spotify with Discord is a simple but powerful feature.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll walk you through everything you need to know about how to connect Spotify to Discord—step by step. We’ll also cover a few extra tips, like how to display your music status, what to do if Spotify isn’t showing up, and how to unlink your account if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Connect Spotify to Discord?
&lt;/h2&gt;

&lt;p&gt;Before we jump into the how-to, let’s look at why you might want to link Spotify with Discord in the first place.&lt;/p&gt;

&lt;p&gt;Here are a few benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share your music status: Others can see what you’re listening to in real-time.&lt;/li&gt;
&lt;li&gt;Listen along with friends: If you have &lt;a href="https://spotipremiiumapk.com/spotify-premium-apk-ios/" rel="noopener noreferrer"&gt;Spotify Premium APK iOS&lt;/a&gt;, you can listen to music in sync with friends who are also Premium users.&lt;/li&gt;
&lt;li&gt;Perfect for gaming sessions: Background music while voice chatting adds to the fun.&lt;/li&gt;
&lt;li&gt;Makes your profile more interesting: It’s a great conversation starter when someone sees you vibing to the same band or artist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites: What You’ll Need
&lt;/h2&gt;

&lt;p&gt;Before getting started, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Spotify account (Free or Premium)&lt;/li&gt;
&lt;li&gt;A Discord account&lt;/li&gt;
&lt;li&gt;The Discord app (desktop or mobile) or access to Discord via web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While both free and premium Spotify accounts can be connected, the “Listen Along” feature is limited to Spotify Premium users.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Connect Spotify to Discord (Desktop)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open Discord and Go to User Settings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Launch the Discord desktop app or log in via browser.&lt;/li&gt;
&lt;li&gt;Click the gear icon ⚙️ next to your username in the lower-left corner. This opens the User Settings menu.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Go to "Connections"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In the left-hand menu, scroll down and select Connections.&lt;/li&gt;
&lt;li&gt;Here, you'll see a list of services you can connect to Discord, such as Twitch, YouTube, Reddit, and Spotify.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Click the Spotify Icon
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Click on the Spotify icon.&lt;/li&gt;
&lt;li&gt;A new browser window will open asking you to log in to Spotify.&lt;/li&gt;
&lt;li&gt;Enter your Spotify credentials and allow the necessary permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Customize Your Settings
&lt;/h3&gt;

&lt;p&gt;Once connected, you’ll see a green Spotify box under your Connections tab. You can toggle the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display on profile – Shows the Spotify icon and currently playing track on your Discord profile.&lt;/li&gt;
&lt;li&gt;Display Spotify as your status – Shows the song you’re currently listening to underneath your username.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg8pvn7wst3z7neqa9qk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg8pvn7wst3z7neqa9qk.png" alt=" " width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it! You’re connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Connect Spotify to Discord (Mobile)
&lt;/h2&gt;

&lt;p&gt;The process is similar on mobile, though the layout is slightly different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Open Discord App
&lt;/h3&gt;

&lt;p&gt;Launch the Discord app on your iOS or Android device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Access User Settings
&lt;/h3&gt;

&lt;p&gt;Tap your profile icon at the bottom right to access User Settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Tap “Connections”
&lt;/h3&gt;

&lt;p&gt;Scroll and tap on Connections. If you’re not already logged in on Spotify through your phone’s browser, you’ll be prompted to sign in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Select Spotify
&lt;/h3&gt;

&lt;p&gt;Tap the Spotify logo and log in with your Spotify credentials when prompted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2u13ok698gj502q4470.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2u13ok698gj502q4470.png" alt=" " width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the connection is successful, your settings will sync across both mobile and desktop platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listen Along Feature
&lt;/h2&gt;

&lt;p&gt;One of the coolest integrations is the “Listen Along” feature. It allows you and your friends to listen to the same song at the same time inside a voice channel or DMs.&lt;/p&gt;

&lt;p&gt;To use Listen Along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure you and your friends have Spotify Premium.&lt;/li&gt;
&lt;li&gt;Play a song on Spotify.&lt;/li&gt;
&lt;li&gt;Your friends will see a “Listen Along” button next to your profile in Discord.&lt;/li&gt;
&lt;li&gt;They can click it to sync playback with you.&lt;/li&gt;
&lt;li&gt;This feature is perfect for hosting mini listening&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>spotify</category>
      <category>discord</category>
    </item>
    <item>
      <title>What is Astrological Profile on Snapchat?</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Thu, 17 Jul 2025 06:06:23 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/what-is-astrological-profile-on-snapchat-1l5b</link>
      <guid>https://forem.com/leonardokaprio/what-is-astrological-profile-on-snapchat-1l5b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmpbxzif5qcvju2y9mf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmpbxzif5qcvju2y9mf5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Snapchat is known for its fun and creative features — from playful filters to interactive stories. But beyond the surface-level fun, it also offers something a bit more personal: the Astrological Profile. This feature, available to users who add their birthday and birth time, gives you insights into your zodiac sign, rising sign, and deeper elements of your natal chart — all within the Snapchat app.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore what Snapchat’s Astrological Profile is, how it works, what it reveals about you, and why many users enjoy sharing and comparing their astrological insights with friends.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Astrological Profile on Snapchat?
&lt;/h2&gt;

&lt;p&gt;The Astrological Profile on Snapchat is a personalized birth chart, also known as a natal chart, which shows the positions of the planets at the exact time and place of your birth. Snapchat uses your birthday, birth time, and birth location to generate this chart and then presents it in an easy-to-understand, friendly layout.&lt;/p&gt;

&lt;p&gt;This profile reveals more than just your Sun sign (like Leo or Scorpio); it also includes your Moon sign, Rising sign (Ascendant), and the planetary placements for Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, and Pluto.&lt;/p&gt;

&lt;p&gt;Snapchat simplifies astrology by providing a breakdown of these signs and what they say about your personality, emotions, communication style, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up Your Astrological Profile on Snapchat
&lt;/h2&gt;

&lt;p&gt;Creating your Astrological Profile on Snapchat is easy and takes just a minute or two:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Snapchat and tap on your Bitmoji (top-left corner).&lt;/li&gt;
&lt;li&gt;Tap the purple zodiac symbol (if you've already entered your birthdate).&lt;/li&gt;
&lt;li&gt;You’ll be asked to input your birth time and birthplace.&lt;/li&gt;
&lt;li&gt;Once you submit, Snapchat will generate your full astrological profile.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don’t see the zodiac icon, make sure your birthday is added to your profile first. You can do this from the settings menu.&lt;/p&gt;

&lt;p&gt;Note: Your information is kept private unless you choose to share it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Information Does Snapchat’s Astrological Profile Show?
&lt;/h2&gt;

&lt;p&gt;Once your profile is ready, Snapchat gives you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sun Sign&lt;br&gt;
Your Sun sign reflects your core identity and personality — what most people associate with zodiac signs. It's based on your birthday.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Moon Sign&lt;br&gt;
This sign governs your emotions and inner self. It shows how you deal with feelings and emotional responses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rising Sign (Ascendant)&lt;br&gt;
The rising sign indicates how others perceive you — your outward style and first impression.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Planetary Placements&lt;br&gt;
Each &lt;a href="https://snapplanetshub.com/" rel="noopener noreferrer"&gt;Snapchat Best Friend planet&lt;/a&gt; is tied to a specific part of your personality:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Mercury – Communication&lt;/li&gt;
&lt;li&gt;Venus – Love and beauty&lt;/li&gt;
&lt;li&gt;Earth - Beauty&lt;/li&gt;
&lt;li&gt;Mars – Drive and energy&lt;/li&gt;
&lt;li&gt;Jupiter – Luck and growth&lt;/li&gt;
&lt;li&gt;Saturn – Discipline and responsibility&lt;/li&gt;
&lt;li&gt;Uranus – Innovation and change&lt;/li&gt;
&lt;li&gt;Neptune – Dreams and intuition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Snapchat gives short, clear explanations of what each placement means, making astrology more approachable even for beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can You See Astrological Compatibility on Snapchat?
&lt;/h2&gt;

&lt;p&gt;Yes! Snapchat also offers Astrological Compatibility with your friends — but only if both of you have completed your profiles. When you tap on a friend’s profile who has also entered their astrological details, you can view a compatibility summary.&lt;/p&gt;

&lt;p&gt;This includes comparisons like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Emotional connection (Moon signs)&lt;/li&gt;
&lt;li&gt;Communication style (Mercury)&lt;/li&gt;
&lt;li&gt;Love and romance (Venus)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll see emojis, short explanations, and how the stars align between you and your friend. It’s a fun way to explore your friendships (or something more).&lt;/p&gt;

&lt;h2&gt;
  
  
  Is It Safe to Share Your Astrological Profile?
&lt;/h2&gt;

&lt;p&gt;Snapchat is clear about user privacy. Your Astrological Profile is not visible to others unless you choose to share it. You can also manage who sees your birthday and zodiac info from your privacy settings.&lt;/p&gt;

&lt;p&gt;If you’re not comfortable sharing your exact birth time or location, you can skip the deeper parts — though that will limit the accuracy of your chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do People Love the Astrological Profile Feature?
&lt;/h2&gt;

&lt;p&gt;Snapchat has taken something often seen as complicated and made it feel light, friendly, and easy to connect with. Here’s why users enjoy it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-discovery: It helps users learn more about themselves.&lt;/li&gt;
&lt;li&gt;Friendship fun: Comparing charts and compatibility is a great icebreaker.&lt;/li&gt;
&lt;li&gt;Aesthetic appeal: The visuals and emojis make astrology fun and digestible.&lt;/li&gt;
&lt;li&gt;Privacy control: You’re always in control of what you share.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're a true believer in astrology or just curious, the Astrological Profile adds a personal touch to your Snapchat experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Snapchat’s Astrological Profile is more than just a novelty. It blends ancient zodiac wisdom with a modern, visual twist — helping users reflect on their traits and connect with others on a deeper level. You don’t need to be an astrology expert to enjoy it, and Snapchat does a great job of guiding you through the process.&lt;/p&gt;

&lt;p&gt;If you haven’t tried it yet, go ahead and explore your birth chart inside the app. You might learn something new about yourself — or find out why you click so well with your best friend!&lt;/p&gt;

</description>
      <category>snapchat</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Playback and Offline Download Issues with Spotify Premium</title>
      <dc:creator>Tariq Mehmood</dc:creator>
      <pubDate>Mon, 06 Jan 2025 07:16:59 +0000</pubDate>
      <link>https://forem.com/leonardokaprio/playback-and-offline-download-issues-with-spotify-premium-1mkc</link>
      <guid>https://forem.com/leonardokaprio/playback-and-offline-download-issues-with-spotify-premium-1mkc</guid>
      <description>&lt;p&gt;I’ve been experiencing issues with &lt;a href="https://thespotifypremium.net/" rel="noopener noreferrer"&gt;Spotify Premium App&lt;/a&gt; that are affecting my overall listening experience. Despite having a stable internet connection, playback often lags or buffers for extended periods, which disrupts the flow of music. Additionally, offline downloads frequently fail or disappear, even though the songs were marked for offline listening. These problems persist across devices, making it difficult to enjoy the premium features I’ve subscribed to.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
