{ "cells": [ { "cell_type": "markdown", "id": "f2446309-25e4-4582-a537-b8292885dcad", "metadata": {}, "source": [ "# Demo: Only positive news" ] }, { "cell_type": "markdown", "id": "123456789-930485093240532940945-0324095320945904325", "metadata": { "tags": [] }, "source": [" _Choose Social Media Platform: Reddit | Discord | __Bluesky__ | No Coding_ "] }, { "cell_type": "markdown", "id": "a2ec8fb0", "metadata": {}, "source": [ "\n", "Let's look at something we could try to do to improve the mental health for our users: Only show positive news!\n", "\n", "We'll use sentiment analysis again, but this time we'll get posts from the news subreddit, but only display the posts with a positive sentiment.\n", "\n", "Would this actually improve someone's mental health? It's hard to say! But we can see something that we might try out if we wanted to improve mental health of our users." ] }, { "cell_type": "markdown", "id": "ae27cd44-3897-4154-b7a1-a5a807bf92c7", "metadata": {}, "source": [ "## Normal Bluesky Setup\n", "\n", "We'll start by doing our normal steps including these helper functions:" ] }, { "cell_type": "markdown", "id": "c4dec0e6-ebb4-4e2f-876f-026a84795760", "metadata": {}, "source": [ "### helper function for atproto links\n", "_NOTE: You don't need to worry about the details of how this works, it just is here to make the code later easier to use._" ] }, { "cell_type": "code", "execution_count": 1, "id": "9e98a8e2-8094-46a4-bced-86df620f12d0", "metadata": {}, "outputs": [], "source": [ "import re #load a \"regular expression\" library for helping to parse text\n", "from atproto import IdResolver # Load the atproto IdResolver library to get offical ATProto user IDs\n", "\n", "# function to convert a post's special atproto \"at\" URI to a weblink url\n", "def getWebLinkFromPost(post):\n", " # Get the user id and post id from the weblink url\n", " match = re.search(r'at://([^/]+)/app.bsky.feed.post/([^/]+)', post.uri)\n", " if not match:\n", " raise ValueError(\"Invalid Bluesky atproto post URL format.\")\n", " user_id, post_id = match.groups()\n", "\n", " post_uri = f\"https://bsky.app/profile/{user_id}/post/{post_id}\"\n", " return post_uri" ] }, { "cell_type": "markdown", "id": "7cf3ab99-3a66-4cec-9fd7-0600d77d976d", "metadata": {}, "source": [ "Now we can continue logging in to Bluesky and look through multiple posts.\n", "### load atproto library" ] }, { "cell_type": "code", "execution_count": 2, "id": "0d45a981-86cd-41f0-bc0a-066afdc985b4", "metadata": { "tags": [] }, "outputs": [], "source": [ "# Load some code called \"Client\" from the \"atproto\" library that will help us work with Bluesky\n", "from atproto import Client" ] }, { "cell_type": "markdown", "id": "c699f603-a604-43b9-af9e-27e3ca60f542", "metadata": {}, "source": [ "### (optional) make a fake Bluesky connection with the fake_atproto library\n", "For testing purposes, we\"ve added this line of code, which loads a fake version of atproto, so it wont actually connect to Bluesky. __If you want to try to actually connect to Bluesky, don't run this line of code.__" ] }, { "cell_type": "code", "execution_count": 3, "id": "24a8ae3d-bc92-4628-b348-75d1ffcf1bfe", "metadata": {}, "outputs": [ { "data": { "text/html": [ "Fake atproto (bsky.app) is replacing the atproto.blue library. Fake atproto doesn't need real passwords, and prevents you from accessing real Bluesky" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%run ../fake_apis/fake_atproto.ipynb" ] }, { "cell_type": "markdown", "id": "19554a16-4085-4980-91e0-ebd3a2d80fca", "metadata": {}, "source": [ "### login to Bluesky" ] }, { "cell_type": "code", "execution_count": 4, "id": "72567e4d-e517-43f1-a949-49fb29120ddf", "metadata": {}, "outputs": [ { "data": { "text/html": [ "Fake atproto is pretending to set up a client connection to: https://bsky.social" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "Fake atproto is pretending log into your account: your_account_name.bsky.social" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Login to Bluesky\n", "# TODO: put your account name and password below\n", "\n", "client = Client(base_url=\"https://bsky.social\")\n", "client.login(\"your_account_name.bsky.social\", \"m#5@_fake_bsky_password_$%Ds\")" ] }, { "cell_type": "markdown", "id": "c46fd9fd-d9d4-4966-9b3b-9c4cfd5ac6b3", "metadata": {}, "source": [ "## Load sentiment analyis code" ] }, { "cell_type": "code", "execution_count": 5, "id": "97cdf7e8-73a9-4b9f-867c-2914e1b6f85f", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package vader_lexicon to\n", "[nltk_data] C:\\Users\\kmthayer\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package vader_lexicon is already up-to-date!\n" ] } ], "source": [ "import nltk\n", "nltk.download([\"vader_lexicon\"])\n", "from nltk.sentiment import SentimentIntensityAnalyzer\n", "sia = SentimentIntensityAnalyzer()" ] }, { "cell_type": "markdown", "id": "544f922d-0b24-4923-b635-c5de0fb39da1", "metadata": {}, "source": [ "## Search for \"news\" \n", "\n", "We'll now run a search for nows and then we'll try to filter for just positive sentiment.\n", "\n", "### search for posts\n", "_Note: If you run this on real Bluesky, we can’t gurantee anything about how offensive what you might find is._" ] }, { "cell_type": "code", "execution_count": 6, "id": "7232d9db-74c3-44c6-a835-6d541fca3e0e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Breaking news: A lovely cat took a nice long nap today!\n", "\n", "Breaking news: Someone said a really mean thing on the internet today!\n", "\n", "Breaking news: Some grandparents made some yummy cookies for all the kids to share!\n", "\n", "Breaking news: All the horrors of the universe revealed at last!\n", "\n" ] } ], "source": [ "search_query = \"news\"\n", "search_results = client.app.bsky.feed.search_posts({'q': search_query}).posts\n", "\n", "# go through each reddit submission\n", "for post in search_results:\n", " print(post.record.text)\n", " print()\n" ] }, { "cell_type": "markdown", "id": "21e1b71b-b881-4227-82f4-76df85e47df0", "metadata": {}, "source": [ "## Search through news submissions and only display good news\n", "Now we will make a different version of the code that computes the sentiment of each submission title and only displays the ones with positive sentiment." ] }, { "cell_type": "code", "execution_count": 7, "id": "a8297615-bb0f-4728-8ff1-c891336335a9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Breaking news: A lovely cat took a nice long nap today!\n", "\n", "Breaking news: Some grandparents made some yummy cookies for all the kids to share!\n", "\n" ] } ], "source": [ "search_query = \"news\"\n", "search_results = client.app.bsky.feed.search_posts({'q': search_query}).posts\n", "\n", "# go through each reddit submission\n", "for post in search_results:\n", " \n", " #calculate sentiment\n", " post_sentiment = sia.polarity_scores(post.record.text)[\"compound\"]\n", " \n", " if(post_sentiment > 0):\n", " print(post.record.text)\n", " print()\n" ] }, { "cell_type": "markdown", "id": "1e7fe660-e47f-4c2d-acf0-c14021c2807f", "metadata": {}, "source": [ "## Try it out on real Bluesky\n", "If you want, you can skip the fake_atrproto step and try your own search on real Bluesky.\n", "\n", "Did it work like you expected?\n", "\n", "You can also only show negative sentiment submissions (sentiment < 0) if you want to see only bad news." ] }, { "cell_type": "code", "execution_count": null, "id": "5857a1cb-321c-459f-91c8-1663cb8a1340", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.5" } }, "nbformat": 4, "nbformat_minor": 5 }