Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions admin/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (

"mu/internal/app"
"mu/internal/auth"
"mu/internal/moderation"
"mu/internal/flag"
)

// Re-export types from moderation subsystem for backward compatibility
// in admin dashboard handlers.
type PostContent = moderation.PostContent
type FlaggedItem = moderation.FlaggedItem
type ContentDeleter = moderation.ContentDeleter
type LLMAnalyzer = moderation.LLMAnalyzer
type PostContent = flag.PostContent
type FlaggedItem = flag.FlaggedItem
type ContentDeleter = flag.ContentDeleter
type LLMAnalyzer = flag.LLMAnalyzer

// Import blog to get new account blog posts - will be set by blog package to avoid circular import
var GetNewAccountBlog func() []PostContent
Expand All @@ -26,15 +26,15 @@ var RefreshBlogCache func()
// Delegated functions — building blocks should import internal/moderation directly.
// These exist only so admin's own handlers can call them.
var (
RegisterDeleter = moderation.RegisterDeleter
SetAnalyzer = moderation.SetAnalyzer
CheckContent = moderation.CheckContent
IsHidden = moderation.IsHidden
AdminFlag = moderation.AdminFlag
RegisterDeleter = flag.RegisterDeleter
SetAnalyzer = flag.SetAnalyzer
CheckContent = flag.CheckContent
IsHidden = flag.IsHidden
AdminFlag = flag.AdminFlag
)

func Load() {
moderation.Load()
flag.Load()
}

// ============================================
Expand Down Expand Up @@ -79,7 +79,7 @@ func FlagHandler(w http.ResponseWriter, r *http.Request) {
}

// Add flag
count, alreadyFlagged, err := moderation.Add(contentType, contentID, flagger)
count, alreadyFlagged, err := flag.Add(contentType, contentID, flagger)
if err != nil {
http.Error(w, "Failed to flag content", http.StatusInternalServerError)
return
Expand All @@ -93,7 +93,7 @@ func FlagHandler(w http.ResponseWriter, r *http.Request) {

// Refresh cache if content was hidden
if count >= 3 {
if deleter, ok := moderation.GetDeleter(contentType); ok {
if deleter, ok := flag.GetDeleter(contentType); ok {
deleter.RefreshCache()
}
}
Expand All @@ -118,7 +118,7 @@ func ModerateHandler(w http.ResponseWriter, r *http.Request) {

_ = acc // acc.Admin is always true here

flaggedItems := moderation.GetAll()
flaggedItems := flag.GetAll()

var itemsList []string
for _, item := range flaggedItems {
Expand All @@ -128,7 +128,7 @@ func ModerateHandler(w http.ResponseWriter, r *http.Request) {
var createdAt string

// Get content from the appropriate handler
if deleter, ok := moderation.GetDeleter(item.ContentType); ok {
if deleter, ok := flag.GetDeleter(item.ContentType); ok {
content := deleter.Get(item.ContentID)
switch item.ContentType {
case "post":
Expand Down Expand Up @@ -319,11 +319,11 @@ func handleModeration(w http.ResponseWriter, r *http.Request) {

switch action {
case "approve":
moderation.Approve(contentType, contentID)
flag.Approve(contentType, contentID)
http.Redirect(w, r, "/admin/moderate", http.StatusSeeOther)

case "delete":
moderation.Delete(contentType, contentID)
flag.Delete(contentType, contentID)
http.Redirect(w, r, "/admin/moderate", http.StatusSeeOther)

case "approve_account":
Expand Down
45 changes: 23 additions & 22 deletions blog/blog.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import (
"mu/internal/app"
"mu/internal/auth"
"mu/internal/data"
"mu/internal/moderation"
"mu/internal/event"
"mu/internal/flag"
"mu/wallet"
)

Expand Down Expand Up @@ -140,12 +141,12 @@ func Load() {
}

// Subscribe to tag generation responses
tagSub := data.Subscribe(data.EventTagGenerated)
tagSub := event.Subscribe(event.EventTagGenerated)
go func() {
for event := range tagSub.Chan {
postID, okID := event.Data["post_id"].(string)
tag, okTag := event.Data["tag"].(string)
eventType, okType := event.Data["type"].(string)
for evt := range tagSub.Chan {
postID, okID := evt.Data["post_id"].(string)
tag, okTag := evt.Data["tag"].(string)
eventType, okType := evt.Data["type"].(string)

if okID && okTag && okType && eventType == "post" {
app.Log("blog", "Received generated tag for post: %s", postID)
Expand Down Expand Up @@ -255,10 +256,10 @@ func Load() {
}()

// Register with moderation subsystem
moderation.RegisterDeleter("post", &postDeleter{})
flag.RegisterDeleter("post", &postDeleter{})
}

// postDeleter implements moderation.ContentDeleter interface
// postDeleter implements flag.ContentDeleter interface
type postDeleter struct{}

func (d *postDeleter) Delete(id string) error {
Expand All @@ -270,7 +271,7 @@ func (d *postDeleter) Get(id string) interface{} {
if post == nil {
return nil
}
return moderation.PostContent{
return flag.PostContent{
Title: post.Title,
Content: post.Content,
Author: post.Author,
Expand All @@ -279,20 +280,20 @@ func (d *postDeleter) Get(id string) interface{} {
}

// GetNewAccountBlogPosts returns blog posts from new accounts for the moderation page.
func GetNewAccountBlogPosts() []moderation.PostContent {
func GetNewAccountBlogPosts() []flag.PostContent {
mutex.RLock()
defer mutex.RUnlock()

var result []moderation.PostContent
var result []flag.PostContent
for _, post := range posts {
// Skip flagged/hidden posts
if moderation.IsHidden("post", post.ID) {
if flag.IsHidden("post", post.ID) {
continue
}

// Only include posts from new accounts
if post.AuthorID != "" && auth.IsNewAccount(post.AuthorID) {
result = append(result, moderation.PostContent{
result = append(result, flag.PostContent{
ID: post.ID,
Title: post.Title,
Content: post.Content,
Expand Down Expand Up @@ -351,7 +352,7 @@ func updateCache() {
updateCacheUnlocked()

// Publish event to refresh home page cache
data.Publish(data.Event{
event.Publish(event.Event{
Type: "blog_updated",
Data: map[string]interface{}{},
})
Expand All @@ -365,7 +366,7 @@ func updateCacheUnlocked() {
for i := 0; i < len(posts) && count < 1; i++ {
post := posts[i]
// Skip flagged posts
if moderation.IsHidden("post", post.ID) {
if flag.IsHidden("post", post.ID) {
continue
}
// Skip private posts (home page shows only public posts)
Expand Down Expand Up @@ -460,7 +461,7 @@ func updateCacheUnlocked() {
var fullList []string
for _, post := range posts {
// Skip flagged posts
if moderation.IsHidden("post", post.ID) {
if flag.IsHidden("post", post.ID) {
continue
}

Expand Down Expand Up @@ -582,7 +583,7 @@ func previewUncached() string {
for i := 0; i < len(posts) && count < 1; i++ {
post := posts[i]
// Skip flagged posts
if moderation.IsHidden("post", post.ID) {
if flag.IsHidden("post", post.ID) {
continue
}
// Skip posts from new accounts (< 24 hours old)
Expand Down Expand Up @@ -725,7 +726,7 @@ func handleGetBlog(w http.ResponseWriter, r *http.Request) {
// Filter out flagged posts and private posts (unless admin)
var visiblePosts []*Post
for _, post := range posts {
if !moderation.IsHidden("post", post.ID) {
if !flag.IsHidden("post", post.ID) {
// Skip private posts for non-admins
if post.Private && !isAdmin {
continue
Expand Down Expand Up @@ -960,8 +961,8 @@ func autoTagPost(postID, title, content string) {
app.Log("blog", "Requesting tag generation for post: %s", postID)

// Publish tag generation request
data.Publish(data.Event{
Type: data.EventGenerateTag,
event.Publish(event.Event{
Type: event.EventGenerateTag,
Data: map[string]interface{}{
"post_id": postID,
"title": title,
Expand Down Expand Up @@ -1232,7 +1233,7 @@ func PostHandler(w http.ResponseWriter, r *http.Request) {
wallet.ConsumeQuota(acc.ID, wallet.OpBlogCreate)

// Run async LLM-based content moderation
go moderation.CheckContent("post", postID, title, content)
go flag.CheckContent("post", postID, title, content)

if app.SendsJSON(r) {
app.RespondJSON(w, map[string]interface{}{
Expand Down Expand Up @@ -1701,7 +1702,7 @@ func handlePost(w http.ResponseWriter, r *http.Request) {
}

// Run async LLM-based content moderation (non-blocking)
go moderation.CheckContent("post", postID, title, content)
go flag.CheckContent("post", postID, title, content)

// Redirect back to posts page
http.Redirect(w, r, "/blog", http.StatusSeeOther)
Expand Down
51 changes: 26 additions & 25 deletions chat/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
"mu/internal/app"
"mu/internal/auth"
"mu/internal/data"
"mu/internal/moderation"
"mu/internal/event"
"mu/internal/flag"
"mu/wallet"
)

Expand Down Expand Up @@ -471,7 +472,7 @@ func getOrCreateRoom(id string) *Room {

// Subscribe to index complete events via channel
go func() {
sub := data.Subscribe(data.EventIndexComplete)
sub := event.Subscribe(event.EventIndexComplete)
defer sub.Close()

// Wait for either index event or timeout
Expand Down Expand Up @@ -834,8 +835,8 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request, room *Room) {
room.mutex.Unlock()

app.Log("chat", "Publishing refresh event for: %s", room.URL)
data.Publish(data.Event{
Type: data.EventRefreshHNComments,
event.Publish(event.Event{
Type: event.EventRefreshHNComments,
Data: map[string]interface{}{
"url": room.URL,
},
Expand Down Expand Up @@ -1017,7 +1018,7 @@ func Load() {
head = app.Head("chat", topics)

// Register LLM analyzer for content moderation
moderation.SetAnalyzer(&llmAnalyzer{})
flag.SetAnalyzer(&llmAnalyzer{})

// Load existing summaries from disk
if b, err := data.LoadFile("chat_summaries.json"); err == nil {
Expand All @@ -1029,12 +1030,12 @@ func Load() {
}

// Subscribe to summary generation requests
summaryRequestSub := data.Subscribe(data.EventGenerateSummary)
summaryRequestSub := event.Subscribe(event.EventGenerateSummary)
go func() {
for event := range summaryRequestSub.Chan {
uri, okUri := event.Data["uri"].(string)
content, okContent := event.Data["content"].(string)
eventType, okType := event.Data["type"].(string)
for evt := range summaryRequestSub.Chan {
uri, okUri := evt.Data["uri"].(string)
content, okContent := evt.Data["content"].(string)
eventType, okType := evt.Data["type"].(string)

if okUri && okContent && okType {
app.Log("chat", "Received summary generation request for %s (%s)", uri, eventType)
Expand All @@ -1053,8 +1054,8 @@ func Load() {
}

// Publish the generated summary
data.Publish(data.Event{
Type: data.EventSummaryGenerated,
event.Publish(event.Event{
Type: event.EventSummaryGenerated,
Data: map[string]interface{}{
"uri": uri,
"summary": summary,
Expand All @@ -1068,20 +1069,20 @@ func Load() {
}()

// Subscribe to tag generation requests
tagRequestSub := data.Subscribe(data.EventGenerateTag)
tagRequestSub := event.Subscribe(event.EventGenerateTag)
go func() {
for event := range tagRequestSub.Chan {
title, _ := event.Data["title"].(string)
content, okContent := event.Data["content"].(string)
eventType, okType := event.Data["type"].(string)
for evt := range tagRequestSub.Chan {
title, _ := evt.Data["title"].(string)
content, okContent := evt.Data["content"].(string)
eventType, okType := evt.Data["type"].(string)

if !okContent || !okType {
continue
}

// Handle blog post tagging (predefined categories)
if eventType == "post" {
postID, ok := event.Data["post_id"].(string)
postID, ok := evt.Data["post_id"].(string)
if !ok {
continue
}
Expand Down Expand Up @@ -1126,8 +1127,8 @@ func Load() {
continue
}

data.Publish(data.Event{
Type: data.EventTagGenerated,
event.Publish(event.Event{
Type: event.EventTagGenerated,
Data: map[string]interface{}{
"post_id": postID,
"tag": tag,
Expand All @@ -1139,11 +1140,11 @@ func Load() {

// Handle note tagging (free-form single tag)
if eventType == "note" {
noteID, ok := event.Data["note_id"].(string)
noteID, ok := evt.Data["note_id"].(string)
if !ok {
continue
}
userID, ok := event.Data["user_id"].(string)
userID, ok := evt.Data["user_id"].(string)
if !ok {
continue
}
Expand All @@ -1170,8 +1171,8 @@ func Load() {
tag = tag[:20]
}

data.Publish(data.Event{
Type: data.EventTagGenerated,
event.Publish(event.Event{
Type: event.EventTagGenerated,
Data: map[string]interface{}{
"note_id": noteID,
"user_id": userID,
Expand Down Expand Up @@ -1577,7 +1578,7 @@ func handlePostChat(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(renderHTML))
}

// llmAnalyzer implements the moderation.LLMAnalyzer interface
// llmAnalyzer implements the flag.LLMAnalyzer interface
type llmAnalyzer struct{}

func (a *llmAnalyzer) Analyze(promptText, question string) (string, error) {
Expand Down
4 changes: 2 additions & 2 deletions home/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"mu/agent"
"mu/internal/app"
"mu/blog"
"mu/internal/data"
"mu/internal/event"
"mu/news"
"mu/markets"
"mu/reminder"
Expand Down Expand Up @@ -748,7 +748,7 @@ func Load() {

// Subscribe to blog update events
go func() {
sub := data.Subscribe("blog_updated")
sub := event.Subscribe("blog_updated")
for range sub.Chan {
ForceRefresh()
}
Expand Down
Loading