fix user script controller

This commit is contained in:
Dylan Knutson
2025-02-25 00:28:02 +00:00
parent ccd5404a10
commit c555c043a9
9 changed files with 82 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -4,10 +4,10 @@ class Domain::Fa::ApiController < ApplicationController
before_action :validate_api_token!
skip_before_action :verify_authenticity_token,
only: %i[enqueue_objects object_statuses]
only: %i[enqueue_objects object_statuses similar_users]
skip_before_action :validate_api_token!,
only: %i[search_user_names object_statuses]
only: %i[search_user_names object_statuses similar_users]
def search_user_names
name = params[:name]

View File

@@ -1,8 +1,12 @@
# typed: true
class PagesController < ApplicationController
skip_before_action :authenticate_user!, only: [:root]
skip_before_action :authenticate_user!, only: %i[root user_script]
def root
render :root
end
def user_script
render :user_script
end
end

View File

@@ -1,6 +1,7 @@
# typed: true
class UserScriptsController < ApplicationController
skip_before_action :authenticate_user!, only: [:get]
skip_before_action :verify_authenticity_token, only: [:get]
ALLOWED_SCRIPTS = %w[object_statuses.user.js furecs.user.js].freeze

View File

@@ -14,12 +14,18 @@
<%= link_to "Telegram",
"https://t.me/DeltaNoises",
target: "_blank",
class: "sky-link" %>
class: "blue-link" %>
-
<%= link_to "BlueSky",
"https://bsky.app/profile/delta.refurrer.com",
target: "_blank",
class: "sky-link" %>
class: "blue-link" %>
</div>
<div class="mt-4 font-light italic text-slate-500">
<b>New:</b>
Try the <%= link_to "FurAffinity User Recommender",
user_script_path,
class: "blue-link" %> user script to discover similar artists and users!
</div>
</div>
</div>

View File

@@ -0,0 +1,59 @@
<div class="mx-auto mt-2 max-w-2xl p-4">
<h1 class="mb-4 text-2xl font-bold">FurAffinity User Recommender</h1>
<div class="prose prose-slate max-w-none">
<p class="mb-4">
The FurAffinity User Recommender is a user script that helps you discover artists and users similar to ones you already enjoy on FurAffinity.
</p>
<h2 class="mt-6 mb-2 text-xl font-semibold">Features</h2>
<ul class="list-disc pl-6">
<li>Adds a "Similar Users" section to FurAffinity user profile pages</li>
<li>Uses tried-and-true algorithms to find similar users, based off of follow lists</li>
<li>Integrates seamlessly with FurAffinity's interface</li>
<li>If you are logged in to FurAffinity, will show you users that you don't already follow</li>
</ul>
<h2 class="mt-6 mb-2 text-xl font-semibold">Installation</h2>
<p class="mb-4">
1. First, install a userscript manager like
<%= link_to "https://www.tampermonkey.net/", target: "_blank", class: "blue-link" do %>
Tampermonkey <i class="fas fa-external-link-alt text-xs"></i>
<% end %>
or
<%= link_to "https://violentmonkey.github.io/", target: "_blank", class: "blue-link" do %>
Violentmonkey <i class="fas fa-external-link-alt text-xs"></i>
<% end %>.
</p>
<p class="mb-4">
2. Then, <%= link_to "click here", "https://refurrer.com/us/furecs.user.js", class: "blue-link" %> to install the script.
</p>
<div class="mt-6 mb-8">
<p class="mb-2 text-sm text-slate-600 italic">Screenshot of the Similar Users section added to a user profile page:</p>
<%= image_tag "furecs/furecs-screenshot.png", alt: "Screenshot showing the Similar Users section added by the script", class: "rounded-lg border border-slate-300 shadow-lg" %>
</div>
<h2 class="mt-6 mb-2 text-xl font-semibold">How it Works</h2>
<p class="mb-4">
Just like how Twitter's "Who to Follow" or Amazon's "Customers Also Bought" features work, this recommender uses patterns in user behavior to make suggestions. For FurAffinity, this is based on who follows who.
</p>
<ul class="list-disc pl-6 mb-4">
<li>A sample of user follows is collected from FurAffinity</li>
<li>A <%= link_to "https://en.wikipedia.org/wiki/Collaborative_filtering", target: "_blank", class: "blue-link" do %>
collaborative filtering <i class="fas fa-external-link-alt text-xs"></i>
<% end %>
model is trained on that dataset, to find other users with similar follow patterns</li>
<li>Users with more similar follow patterns are ranked higher in the recommendations</li>
<li>This approach works because people tend to follow users with similar content and styles</li>
</ul>
<p class="mb-4">
Think of it like this: if many people who follow your favorite artist also follow another artist, there's a good chance you'll enjoy that other artist's work too!
</p>
<h2 class="mt-6 mb-2 text-xl font-semibold">Just To Note</h2>
<p class="mb-4">
The recommendations are based on a snapshot of FurAffinity follow lists that is periodically updated. The current model was last updated on January 10, 2025. This means that very recent changes to who follows who might not be reflected in the recommendations yet.
</p>
<div class="mt-8 rounded-lg bg-slate-100 p-4">
<p class="italic text-slate-600">
For support or suggestions, contact @DeltaNoises on
<%= link_to "Telegram", "https://t.me/DeltaNoises", target: "_blank", class: "blue-link" %>.
</p>
</div>
</div>
</div>

View File

@@ -9,6 +9,7 @@ Rails.application.routes.draw do
sessions: "users/sessions",
}
root to: "pages#root"
get "user-script", to: "pages#user_script", as: :user_script
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
namespace :api do

View File

@@ -17,8 +17,6 @@
// ==/UserScript==
'use strict';
// const HOST = 'https://refurrer.com';
const HOST = 'http://localhost:3001';
interface SimilarUserList {
name: string;
@@ -58,6 +56,8 @@ function urlNameFromUserHref(href: string) {
}
async function faUserRecs(): Promise<void> {
const HOST = 'https://refurrer.com';
if (!window.location.pathname.startsWith('/user/')) {
return null;
}

View File

@@ -70,15 +70,15 @@ interface LiveEntityStatTable {
}
async function faObjectStatuses() {
function isNotNull<T>(val: T | null | undefined): val is T {
return val != null;
}
const HOST = 'refurrer.com';
const LIGHT_BG_COLOR = '#353b45';
const DARK_BG_COLOR = '#20242a';
const LIGHT_BORDER_COLOR = '#69697d';
function isNotNull<T>(val: T | null | undefined): val is T {
return val != null;
}
function setupNavbar(): HTMLElement {
const navbarCssElem = document.createElement('style');
const navbarHeight = '60px';