tests for is_nsfw_live_announcement

This commit is contained in:
CJ_Clippy 2025-03-15 17:04:57 -08:00
parent 2ffa655163
commit 719fe79f73
11 changed files with 424 additions and 89 deletions

@ -10,6 +10,7 @@ defmodule Bright.ObanWorkers.ProcessPosts do
alias Bright.Socials.XPost alias Bright.Socials.XPost
alias Bright.Streams.Stream alias Bright.Streams.Stream
alias Bright.Platforms.Platform alias Bright.Platforms.Platform
alias Bright.Platforms
import Ecto.Query import Ecto.Query
require Logger require Logger
@ -23,10 +24,10 @@ defmodule Bright.ObanWorkers.ProcessPosts do
def perform(%Oban.Job{args: %{}}) do def perform(%Oban.Job{args: %{}}) do
Logger.info(">>>> Process Posts is performing.") Logger.info(">>>> Process Posts is performing.")
known_platforms = Repo.all(Platform) known_platforms = Platform |> Repo.all() |> Repo.preload(:platform_aliases)
{num, nil} = {num, nil} =
get_unprocessed_posts() XPost.get_unprocessed_posts()
|> then(fn posts -> |> then(fn posts ->
if posts == [] do if posts == [] do
Logger.info("No unprocessed posts found") Logger.info("No unprocessed posts found")
@ -42,19 +43,17 @@ defmodule Bright.ObanWorkers.ProcessPosts do
def process_post(post, known_platforms) do def process_post(post, known_platforms) do
with platforms <- XPost.get_platforms_mentioned(post, known_platforms), with platforms <- XPost.get_platforms_mentioned(post, known_platforms),
true <- is_nsfw_live_annoucement?(post, platforms, known_platforms), true <- XPost.is_nsfw_live_announcement?(post, platforms, known_platforms),
{:ok, _stream} <- create_stream(post, platforms) do {:ok, _stream} <- create_stream(post, platforms) do
:ok :ok
else else
_ -> :ok idk ->
end Logger.debug(
end "process_post did not find a nsfw live announcement. post=#{inspect(post)} known_platforms=#{inspect(known_platforms)}"
)
def get_unprocessed_posts() do :ok
XPost end
|> where([p], is_nil(p.processed_at))
|> preload(:vtuber)
|> Repo.all()
end end
@doc """ @doc """
@ -99,43 +98,4 @@ defmodule Bright.ObanWorkers.ProcessPosts do
# No posts to update # No posts to update
defp mark_posts_as_processed(_), do: :ok defp mark_posts_as_processed(_), do: :ok
@doc """
Is the post a valid NSFW livestream announcement?
Eligibility requirements.
To be considered a NSFW live announcement, a post must satisfy all the following conditions.
* The post is authored by the lewdtuber
* The post mentions a NSFW platform
* The post does not contain any URLs to SFW streaming platforms.
"""
def is_nsfw_live_annoucement?(%XPost{vtuber: vtuber} = post, platforms, known_platforms) do
Logger.debug("Checking if post is NSFW live announcement: #{inspect(post)}")
nsfw_platforms = Enum.filter(known_platforms, & &1.nsfw?)
sfw_platforms = Enum.reject(known_platforms, & &1.nsfw?)
conditions = [
{:authored_by_vtuber?, not is_nil(vtuber)},
{:contains_nsfw_link?,
Enum.any?(platforms, fn plat -> Enum.any?(nsfw_platforms, &match_platform?(plat, &1)) end)},
{:no_sfw_link?,
not Enum.any?(platforms, fn plat ->
Enum.any?(sfw_platforms, &match_platform?(plat, &1))
end)}
]
Enum.reduce_while(conditions, true, fn {label, condition}, _acc ->
if condition do
{:cont, true}
else
Logger.debug("NSFW announcement check failed at: #{label}")
{:halt, false}
end
end)
end
defp match_platform?(plat, platform), do: String.contains?(plat, &URI.parse(&1.url).hostname)
end end

@ -38,8 +38,8 @@ defmodule Bright.Platforms do
""" """
def get_platform!(id) do def get_platform!(id) do
Platform Platform
|> Repo.get!(Platform, id) |> Repo.get!(id)
|> Repo.preload([:platform_aliases]) |> Repo.preload(:platform_aliases)
end end
@doc """ @doc """
@ -125,4 +125,15 @@ defmodule Bright.Platforms do
|> PlatformAlias.changeset(attrs) |> PlatformAlias.changeset(attrs)
|> Repo.insert() |> Repo.insert()
end end
def match_platform?(a, b) do
URI.parse(a.url).host === URI.parse(b.url).host
end
@doc """
Do any of the A platforms match any of the B platforms?
"""
def contains_platform?(a, b) do
Enum.any?(a, fn plat -> Enum.any?(b, &match_platform?(plat, &1)) end)
end
end end

@ -18,5 +18,6 @@ defmodule Bright.Platforms.Platform do
platform platform
|> cast(attrs, [:name, :url, :slug]) |> cast(attrs, [:name, :url, :slug])
|> validate_required([:name, :url, :slug]) |> validate_required([:name, :url, :slug])
|> unique_constraint(:name)
end end
end end

@ -1,7 +1,26 @@
defmodule Bright.Socials do defmodule Bright.Socials do
alias Bright.Socials.XPost
alias Bright.Repo
@moduledoc """ @moduledoc """
Socials context, for functions for interacting with social media platforms Socials context, for functions for interacting with social media platforms
""" """
@doc """
Creates a x_post.
## Examples
iex> x_post(%{field: value})
{:ok, %XPost{}}
iex> x_post(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_x_post(attrs \\ %{}) do
%XPost{}
|> XPost.changeset(attrs)
|> Repo.insert()
end
end end

@ -5,6 +5,7 @@ defmodule Bright.Socials.XPost do
alias Bright.Vtubers.Vtuber alias Bright.Vtubers.Vtuber
alias Bright.Socials.{XPost, RSSParser} alias Bright.Socials.{XPost, RSSParser}
alias Bright.Platforms.Platform alias Bright.Platforms.Platform
alias Bright.Platforms
alias Quinn alias Quinn
require Logger require Logger
@ -66,6 +67,17 @@ defmodule Bright.Socials.XPost do
end end
end end
def authored_by_vtuber?(x_post, vtuber) do
x_post.vtuber_id === vtuber.id
end
def get_unprocessed_posts() do
XPost
|> where([p], is_nil(p.processed_at))
|> Repo.all()
|> Repo.preload(:vtuber)
end
def extract_hostname(url) do def extract_hostname(url) do
uri = URI.parse(url) uri = URI.parse(url)
uri.host || "" uri.host || ""
@ -113,6 +125,45 @@ defmodule Bright.Socials.XPost do
def get_platforms_mentioned(raw_text, platforms) do def get_platforms_mentioned(raw_text, platforms) do
Enum.filter(platforms, &includes_platform?(raw_text, &1)) Enum.filter(platforms, &includes_platform?(raw_text, &1))
end end
@doc """
Is the post a valid NSFW livestream announcement?
Eligibility requirements.
To be considered a NSFW live announcement, a post must satisfy all the following conditions.
* The post is authored by the lewdtuber
* The post does not contain, "VOD/i"
* The post mentions a NSFW platform
* The post does not mention any SFW streaming platform.
"""
def is_nsfw_live_announcement?(
%XPost{vtuber: vtuber} = post,
mentioned_platforms,
known_platforms
) do
Logger.debug("Checking if post is NSFW live announcement: #{inspect(post)}")
nsfw_platforms = Enum.filter(known_platforms, & &1.nsfw)
sfw_platforms = Enum.reject(known_platforms, & &1.nsfw)
conditions = [
# {:authored_by_vtuber?, XPost.authored_by_vtuber?(post, vtuber)}, # This one might not make sense. I think we only get posts from the vtuber's feed
{:not_vod?, not String.contains?(String.downcase(post.raw), "vod")},
{:contains_nsfw_link?, Platforms.contains_platform?(mentioned_platforms, nsfw_platforms)},
{:no_sfw_link?, not Platforms.contains_platform?(mentioned_platforms, sfw_platforms)}
]
Enum.reduce_while(conditions, true, fn {label, condition}, _acc ->
if condition do
{:cont, true}
else
Logger.debug("NSFW announcement check failed at: #{label}")
{:halt, false}
end
end)
end
end end
defimpl Phoenix.HTML.Safe, for: Bright.Socials.XPost do defimpl Phoenix.HTML.Safe, for: Bright.Socials.XPost do

@ -7,47 +7,82 @@ defmodule Bright.ProcessPostsTest do
alias Bright.Streams alias Bright.Streams
alias Bright.Streams.Stream alias Bright.Streams.Stream
alias Bright.Platforms.Platform alias Bright.Platforms.Platform
alias Bright.Platforms
alias Bright.VtubersFixtures alias Bright.VtubersFixtures
alias Bright.Socials.XPost alias Bright.Socials.XPost
alias Bright.Socials
setup do setup do
vtuber = Bright.VtubersFixtures.vtuber_fixture() vtuber = Bright.VtubersFixtures.vtuber_fixture()
%Platform{ Platforms.create_platform(%{
name: "Fansly", name: "Fansly",
slug: "fansly", slug: "fansly",
url: "https://fansly.com", url: "https://fansly.com",
nsfw: true nsfw: true
} })
|> Repo.insert!()
%Platform{ Platforms.create_platform(%{
name: "Twitch", name: "Twitch",
slug: "twitch", slug: "twitch",
url: "https://twitch.tv", url: "https://twitch.tv",
nsfw: false nsfw: false
} })
|> Repo.insert!()
%Platform{ Platforms.create_platform(%{
name: "OnlyFans", name: "OnlyFans",
slug: "onlyfans", slug: "onlyfans",
url: "https://onlyfans.com", url: "https://onlyfans.com",
nsfw: true nsfw: true
} })
|> Repo.insert!()
posts = Platforms.create_platform(%{
for i <- 1..3 do name: "Chaturbate",
%XPost{ slug: "chaturbate",
raw: "Raw content #{i}", url: "https://chaturbate.com",
url: "https://example.com/post#{i}", nsfw: true
date: DateTime.utc_now(:second), })
processed_at: nil,
vtuber_id: vtuber.id posts = [
} # these posts are valid nsfw livestream announcements
|> Repo.insert!() Socials.create_x_post(%{
end raw: "I'm going live! fansly.com/fakename <3",
url: "https://x.com/fakename/status/283498235",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
}),
Socials.create_x_post(%{
raw: "gm! tiem for sex breakfast https://onlyfans.com/fakename",
url: "https://x.com/fakename/status/283498234",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
}),
Socials.create_x_post(%{
raw: "ero strim rn http://chaturbate.com/fakename",
url: "https://x.com/fakename/status/283498232",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
}),
# these posts are NOT valid livestream invitations
Socials.create_x_post(%{
raw: "Let's play a game http://twitch.tv/fakename",
url: "https://x.com/fakename/status/283498343",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
}),
Socials.create_x_post(%{
raw:
"Be sure to follow me on my socials http://chaturbate.com/fakename http://twitch.tv/fakename http://onlyfans.com/fakename http://linktree.com/fakename",
url: "https://x.com/fakename/status/283498349",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
})
]
{:ok, posts: posts, vtuber: vtuber} {:ok, posts: posts, vtuber: vtuber}
end end

@ -2,31 +2,29 @@ defmodule Bright.PlatformsTest do
use Bright.DataCase use Bright.DataCase
alias Bright.Platforms alias Bright.Platforms
alias Bright.Platforms.Platform
describe "platforms" do describe "platforms" do
alias Bright.Platforms.Platform
import Bright.PlatformsFixtures import Bright.PlatformsFixtures
@invalid_attrs %{name: 7, url: 7, icon: 7} @invalid_attrs %{name: 7, url: 7}
test "list_platforms/0 returns all platforms" do test "list_platforms/0 returns all platforms" do
platform = platform_fixture() platform = platform_fixture()
assert Platforms.list_platforms() == [platform] assert Platforms.list_platforms() |> length === 1
end end
test "get_platform!/1 returns the platform with given id" do test "get_platform!/1 returns the platform with given id" do
platform = platform_fixture() platform = platform_fixture(%{name: "Chaturbate"})
assert Platforms.get_platform!(platform.id) == platform assert Platforms.get_platform!(platform.id).name == platform.name
end end
test "create_platform/1 with valid data creates a platform" do test "create_platform/1 with valid data creates a platform" do
valid_attrs = %{name: "some name", url: "some url", icon: "<svg></svg>"} valid_attrs = %{name: "some name", url: "some url", slug: "some_slug"}
assert {:ok, %Platform{} = platform} = Platforms.create_platform(valid_attrs) assert {:ok, %Platform{} = platform} = Platforms.create_platform(valid_attrs)
assert platform.name == "some name" assert platform.name == "some name"
assert platform.url == "some url" assert platform.url == "some url"
assert platform.icon == "<svg></svg>"
end end
test "create_platform/1 with invalid data returns error changeset" do test "create_platform/1 with invalid data returns error changeset" do
@ -38,20 +36,17 @@ defmodule Bright.PlatformsTest do
update_attrs = %{ update_attrs = %{
name: "some updated name", name: "some updated name",
url: "https://example.com", url: "https://example.com"
icon: "<svg>blah</svg>"
} }
assert {:ok, %Platform{} = platform} = Platforms.update_platform(platform, update_attrs) assert {:ok, %Platform{} = platform} = Platforms.update_platform(platform, update_attrs)
assert platform.name == "some updated name" assert platform.name == "some updated name"
assert platform.url == "https://example.com" assert platform.url == "https://example.com"
assert platform.icon == "<svg>blah</svg>"
end end
test "update_platform/2 with invalid data returns error changeset" do test "update_platform/2 with invalid data returns error changeset" do
platform = platform_fixture() platform = platform_fixture()
assert {:error, %Ecto.Changeset{}} = Platforms.update_platform(platform, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = platform |> Platforms.update_platform(@invalid_attrs)
assert platform == Platforms.get_platform!(platform.id)
end end
test "delete_platform/1 deletes the platform" do test "delete_platform/1 deletes the platform" do
@ -65,4 +60,39 @@ defmodule Bright.PlatformsTest do
assert %Ecto.Changeset{} = Platforms.change_platform(platform) assert %Ecto.Changeset{} = Platforms.change_platform(platform)
end end
end end
describe "match_platform?/2" do
test "compares a platform with another and returns true if the two are the same platform" do
platformA = %Platform{name: "Twitch", url: "https://twitch.tv"}
platformB = %Platform{name: "Chaturbate", url: "https://chaturbate.com"}
platformC = %Platform{name: "Twitch", url: "https://twitch.tv"}
assert Platforms.match_platform?(platformA, platformC)
assert not Platforms.match_platform?(platformA, platformB)
end
end
describe "contains_platform?/2" do
@tag :unit
test "accepts a list of platforms and returns true if one of them match any of a list of given platform" do
platformA = %Platform{name: "Twitch", url: "https://twitch.tv"}
platformB = %Platform{name: "Chaturbate", url: "https://chaturbate.com"}
platformC = %Platform{name: "Twitch", url: "https://twitch.tv"}
assert Platforms.contains_platform?([platformA], [platformB, platformC])
assert not Platforms.contains_platform?([platformA], [platformB])
end
@tag :unit
test "returns true if configured to return true for SFW platforms" do
platformA = %Platform{name: "Twitch", url: "https://twitch.tv"}
platformB = %Platform{name: "YouTube", url: "https://youtube.com"}
assert Platforms.contains_platform?([platformA], [platformB, platformA])
end
@tag :unit
test "returns false if matching against an empty list" do
platformA = %Platform{name: "Twitch", url: "https://twitch.tv"}
assert not Platforms.contains_platform?([platformA], [])
assert not Platforms.contains_platform?([], [platformA])
end
end
end end

@ -2,8 +2,10 @@ defmodule Bright.XPostTest do
use Bright.DataCase use Bright.DataCase
alias Bright.Socials.XPost alias Bright.Socials.XPost
alias Bright.Socials
alias Bright.Vtubers
alias Bright.Vtubers.Vtuber alias Bright.Vtubers.Vtuber
alias Bright.XPostsFixtures alias Bright.{SocialsFixtures, VtubersFixtures, XPostsFixtures, PlatformsFixtures}
alias Bright.Platforms.{Platform, PlatformAlias} alias Bright.Platforms.{Platform, PlatformAlias}
alias Bright.Platforms alias Bright.Platforms
alias Bright.VultrAI alias Bright.VultrAI
@ -11,6 +13,31 @@ defmodule Bright.XPostTest do
@sample_feed "https://rss.app/feeds/FhPetvUY036xiFau.xml" @sample_feed "https://rss.app/feeds/FhPetvUY036xiFau.xml"
describe "authored_by_vtuber?/2" do
@tag :unit
test "returns true when the given post is authored by the given vtuber" do
vtuber = VtubersFixtures.vtuber_fixture(%{name: "ProjektMelody", slug: "projektmelody"})
x_post = SocialsFixtures.x_post_fixture(%{vtuber_id: vtuber.id})
assert XPost.authored_by_vtuber?(x_post, vtuber)
end
@tag :unit
test "returns false when the given post is NOT authored by the given vtuber" do
vtuber = VtubersFixtures.vtuber_fixture(%{name: "ProjektMelody", slug: "projektmelody"})
vtuberB = VtubersFixtures.vtuber_fixture(%{name: "Vex", slug: "vex"})
{:ok, x_post} =
Socials.create_x_post(%{
raw: "test",
url: "https://x.com/projektmelody/status/1234",
date: DateTime.utc_now(:second),
vtuber_id: vtuber.id
})
assert not XPost.authored_by_vtuber?(x_post, vtuberB)
end
end
describe "get_new_posts" do describe "get_new_posts" do
@tag :integration @tag :integration
test "get_new_posts/1 with URL" do test "get_new_posts/1 with URL" do
@ -35,6 +62,36 @@ defmodule Bright.XPostTest do
end end
end end
describe "get_unprocessed_posts/0" do
setup do
vtuber =
%Vtuber{
display_name: "Some Vtuber",
slug: "some-vtuber"
}
|> Repo.insert!()
posts =
for i <- 1..3 do
%XPost{
raw: "Raw content #{i}",
url: "https://example.com/post#{i}",
date: DateTime.utc_now(:second),
processed_at: nil,
vtuber_id: vtuber.id
}
|> Repo.insert!()
end
{:ok, posts: posts}
end
@tag :unit
test "gets posts with nil processed_at" do
assert length(XPost.get_unprocessed_posts()) == 3
end
end
describe "includes_alias?/2" do describe "includes_alias?/2" do
setup do setup do
ytmnd = ytmnd =
@ -289,5 +346,100 @@ defmodule Bright.XPostTest do
assert Enum.sort(actual_platform_names) == Enum.sort(expected_platform_names) assert Enum.sort(actual_platform_names) == Enum.sort(expected_platform_names)
end end
@tag :unit
test "post with a platform alias" do
known_platforms = Platforms.list_platforms()
expected_platform_names = ["Fansly"]
actual_platform_names =
XPost.get_platforms_mentioned(
XPostsFixtures.fixture_live_4() |> Map.get(:raw),
known_platforms
)
|> Enum.map(& &1.name)
assert actual_platform_names == expected_platform_names
end
end
describe "is_nsfw_live_announcement?/3" do
setup do
vtuber = VtubersFixtures.vtuber_fixture()
{:ok, x_post} =
Socials.create_x_post(%{
raw: "I'm going live https://twitch.tv/bigchungus",
url: "https://x.com/bigchungus/status/1234",
date: DateTime.utc_now(:second),
vtuber_id: vtuber.id
})
x_post = Repo.preload(x_post, :vtuber)
known_platforms = PlatformsFixtures.known_platforms_fixture()
mentioned_platforms = [
PlatformsFixtures.onlyfans_fixture(),
PlatformsFixtures.chaturbate_fixture(),
PlatformsFixtures.fansly_fixture()
]
{:ok,
vtuber: vtuber,
x_post: x_post,
known_platforms: known_platforms,
mentioned_platforms: mentioned_platforms}
end
@tag :integration
test "should return false when receiving a XPost linking to a SFW platform", %{
vtuber: vtuber,
x_post: x_post,
known_platforms: known_platforms
} do
mentioned_platforms = [
PlatformsFixtures.twitch_fixture(),
PlatformsFixtures.fansly_fixture(),
PlatformsFixtures.onlyfans_fixture(),
PlatformsFixtures.chaturbate_fixture()
]
assert not XPost.is_nsfw_live_announcement?(x_post, mentioned_platforms, known_platforms)
end
test "should return true when receiving an XPost with only Chaturbate mentioned", %{
vtuber: vtuber,
x_post: x_post,
known_platforms: known_platforms,
mentioned_platforms: mentioned_platforms
} do
mentioned_platforms = [
PlatformsFixtures.chaturbate_fixture()
]
assert XPost.is_nsfw_live_announcement?(x_post, mentioned_platforms, known_platforms)
end
test "should return true when receiving an XPost with only NSFW platforms mentioned", %{
vtuber: vtuber,
x_post: x_post,
known_platforms: known_platforms,
mentioned_platforms: mentioned_platforms
} do
assert XPost.is_nsfw_live_announcement?(x_post, mentioned_platforms, known_platforms)
end
test "should return false when receiving an XPost with vod/i", %{
vtuber: vtuber,
known_platforms: known_platforms
} do
x_post = %XPost{
raw:
"IRL JOI handcam stream! Listen to my instructions and stroke your cock for me until you cum 🍆💦\n\nThe rest of the VOD is available here for Tier 1 subscribers or for $10! 💛\n▶️ https://fansly.com/post/755934614"
}
mentioned_platforms = [PlatformsFixtures.fansly_fixture()]
assert not XPost.is_nsfw_live_announcement?(x_post, mentioned_platforms, known_platforms)
end
end end
end end

@ -4,16 +4,16 @@ defmodule Bright.PlatformsFixtures do
entities via the `Bright.Platforms` context. entities via the `Bright.Platforms` context.
""" """
def platform_fixture(attrs \\ %{}) alias Bright.Platforms.Platform
@doc """ @doc """
Generate a platform. Generate a platform.
""" """
def platform_fixture(attrs) do def platform_fixture(attrs \\ %{}) do
{:ok, platform} = {:ok, platform} =
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
icon: "some icon", slug: "some_slug",
name: "some name", name: "some name",
url: "some url" url: "some url"
}) })
@ -21,4 +21,49 @@ defmodule Bright.PlatformsFixtures do
platform platform
end end
def known_platforms_fixture() do
[
twitch_fixture(),
fansly_fixture(),
chaturbate_fixture(),
onlyfans_fixture(),
linktree_fixture(),
discord_fixture(),
carrd_fixture(),
throne_fixture()
]
end
def twitch_fixture() do
%Platform{name: "Twitch", slug: "twitch", url: "https://twitch.tv", nsfw: false}
end
def fansly_fixture() do
%Platform{name: "Fansly", slug: "fansly", url: "https://fansly.com", nsfw: true}
end
def chaturbate_fixture() do
%Platform{name: "Chaturbate", slug: "chaturbate", url: "https://chaturbate.com", nsfw: true}
end
def onlyfans_fixture() do
%Platform{name: "OnlyFans", slug: "onlyfans", url: "https://onlyfans.com", nsfw: true}
end
def linktree_fixture() do
%Platform{name: "Linktree", slug: "linktree", url: "https://linktr.ee", nsfw: false}
end
def discord_fixture() do
%Platform{name: "Discord", slug: "discord", url: "https://discord.com", nsfw: false}
end
def carrd_fixture() do
%Platform{name: "Carrd", slug: "carrd", url: "https://carrd.co", nsfw: false}
end
def throne_fixture() do
%Platform{name: "Throne", slug: "throne", url: "https://throne.com", nsfw: false}
end
end end

@ -0,0 +1,23 @@
defmodule Bright.SocialsFixtures do
@moduledoc """
This module defines test helpers for creating
entities via the `Bright.Socials` context.
"""
@doc """
Generate a platform.
"""
def x_post_fixture(attrs \\ %{}) do
{:ok, x_post} =
attrs
|> Enum.into(%{
raw: "some raw text",
url: "https://x.com/fakeuser/status/9876",
processed_at: nil,
date: DateTime.utc_now(:second)
})
|> Bright.Socials.create_x_post()
x_post
end
end

@ -82,6 +82,14 @@ defmodule Bright.XPostsFixtures do
} }
end end
def fixture_live_4() do
%XPost{
raw: "http://melody.buzz",
date: ~U[2025-05-05T05:05:05.000Z],
url: "https://x.com/ProjektMelody/status/5555"
}
end
@doc """ @doc """
Generates a basic x_post fixture. Generates a basic x_post fixture.
""" """