commit
07dc912624
18 changed files with 1023 additions and 111 deletions
51
BSLManager.md
Normal file
51
BSLManager.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# BSLManager
|
||||||
|
|
||||||
|
A CLI is provided in the Docker image so that admins can manage their instance.
|
||||||
|
|
||||||
|
## Access to the CLI
|
||||||
|
|
||||||
|
Since the CLI is packaged into the docker image, you'll have to open a shell from the container. To do so, list first your running containers:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker ps
|
||||||
|
```
|
||||||
|
|
||||||
|
This should display you something equivalent to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
3734c41af5a7 postgres:9.6 "docker-entrypoint.s…" 2 weeks ago Up 2 weeks 5432/tcp db_1
|
||||||
|
be6870fe103e nicolasconstant/birdsitelive:latest "dotnet BirdsiteLive…" 6 weeks ago Up 2 weeks 443/tcp, 0.0.0.0:5000->80/tcp birdsitelive
|
||||||
|
```
|
||||||
|
|
||||||
|
Find the BSL container and keep the ID, here it's `be6870fe103e`. And you only need the three first char to identify it, so we'll be using `be6`.
|
||||||
|
|
||||||
|
Then open a shell inside the container (change `be6` with your own id):
|
||||||
|
|
||||||
|
```
|
||||||
|
docker exec -it be6 /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
And you should now be inside the container, and all you have to do is calling the CLI:
|
||||||
|
|
||||||
|
```
|
||||||
|
./BSLManager
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting up the CLI
|
||||||
|
|
||||||
|
The manager will ask you to provide information about the database and the instance.
|
||||||
|
Those must be same than the ones in the `docker-compose.yml` file.
|
||||||
|
Provide the information, review it and validate it. Then the CLI UI should shows up.
|
||||||
|
|
||||||
|
## Using the CLI
|
||||||
|
|
||||||
|
You can navigate between the sections with the arrows and tab keys.
|
||||||
|
|
||||||
|
The **filter** permits to filter the list of users with a pattern.
|
||||||
|
|
||||||
|
All users have their followings count provided next to them.
|
||||||
|
You can select any user by using the up/down arrow keys and hitting the `Enter` key, this will display more information about the user.
|
||||||
|
You can also remove a user and all their followings by hitting the `Del` key. You will be prompted by a confirmation message, and you'll be able to remove this user.
|
||||||
|
|
||||||
|
Deleting users having a lots of followings can take some time: after the prompt has closed the process is still running and will update the list after that. Let the software do its thing and it will go through.
|
|
@ -1,17 +1,12 @@
|
||||||
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
|
FROM mcr.microsoft.com/dotnet/aspnet:3.1-buster-slim AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 443
|
EXPOSE 443
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
|
FROM mcr.microsoft.com/dotnet/sdk:3.1-buster AS publish
|
||||||
COPY ./src/ ./src/
|
COPY ./src/ ./src/
|
||||||
RUN dotnet restore "/src/BirdsiteLive/BirdsiteLive.csproj"
|
|
||||||
RUN dotnet restore "/src/BSLManager/BSLManager.csproj"
|
|
||||||
RUN dotnet build "/src/BirdsiteLive/BirdsiteLive.csproj" -c Release -o /app/build
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
RUN dotnet publish "/src/BirdsiteLive/BirdsiteLive.csproj" -c Release -o /app/publish
|
RUN dotnet publish "/src/BirdsiteLive/BirdsiteLive.csproj" -c Release -o /app/publish
|
||||||
RUN dotnet publish "/src/BSLManager/BSLManager.csproj" -r linux-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -c Release -o /app/publish
|
RUN dotnet publish "/src/BSLManager/BSLManager.csproj" -r linux-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -c Release -o /app/publish
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ You can find an official (and temporary) instance here: [beta.birdsite.live](htt
|
||||||
|
|
||||||
I'm providing a [docker build](https://hub.docker.com/r/nicolasconstant/birdsitelive). To install it on your own server, please follow [those instructions](https://github.com/NicolasConstant/BirdsiteLive/blob/master/INSTALLATION.md). More [options](https://github.com/NicolasConstant/BirdsiteLive/blob/master/VARIABLES.md) are also available.
|
I'm providing a [docker build](https://hub.docker.com/r/nicolasconstant/birdsitelive). To install it on your own server, please follow [those instructions](https://github.com/NicolasConstant/BirdsiteLive/blob/master/INSTALLATION.md). More [options](https://github.com/NicolasConstant/BirdsiteLive/blob/master/VARIABLES.md) are also available.
|
||||||
|
|
||||||
|
Also a [CLI](https://github.com/NicolasConstant/BirdsiteLive/blob/master/BSLManager.md) is available for adminitrative tasks.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the AGPLv3 License - see [LICENSE](https://github.com/NicolasConstant/BirdsiteLive/blob/master/LICENSE) for details.
|
This project is licensed under the AGPLv3 License - see [LICENSE](https://github.com/NicolasConstant/BirdsiteLive/blob/master/LICENSE) for details.
|
||||||
|
|
244
src/BSLManager/App.cs
Normal file
244
src/BSLManager/App.cs
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
using BirdsiteLive.DAL.Models;
|
||||||
|
using BirdsiteLive.Moderation.Actions;
|
||||||
|
using BSLManager.Domain;
|
||||||
|
using BSLManager.Tools;
|
||||||
|
using Terminal.Gui;
|
||||||
|
|
||||||
|
namespace BSLManager
|
||||||
|
{
|
||||||
|
public class App
|
||||||
|
{
|
||||||
|
private readonly IFollowersDal _followersDal;
|
||||||
|
private readonly IRemoveFollowerAction _removeFollowerAction;
|
||||||
|
|
||||||
|
private readonly FollowersListState _state = new FollowersListState();
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public App(IFollowersDal followersDal, IRemoveFollowerAction removeFollowerAction)
|
||||||
|
{
|
||||||
|
_followersDal = followersDal;
|
||||||
|
_removeFollowerAction = removeFollowerAction;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Application.Init();
|
||||||
|
var top = Application.Top;
|
||||||
|
|
||||||
|
// Creates the top-level window to show
|
||||||
|
var win = new Window("BSL Manager")
|
||||||
|
{
|
||||||
|
X = 0,
|
||||||
|
Y = 1, // Leave one row for the toplevel menu
|
||||||
|
|
||||||
|
// By using Dim.Fill(), it will automatically resize without manual intervention
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = Dim.Fill()
|
||||||
|
};
|
||||||
|
|
||||||
|
top.Add(win);
|
||||||
|
|
||||||
|
// Creates a menubar, the item "New" has a help menu.
|
||||||
|
var menu = new MenuBar(new MenuBarItem[]
|
||||||
|
{
|
||||||
|
new MenuBarItem("_File", new MenuItem[]
|
||||||
|
{
|
||||||
|
new MenuItem("_Quit", "", () =>
|
||||||
|
{
|
||||||
|
if (Quit()) top.Running = false;
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
//new MenuBarItem ("_Edit", new MenuItem [] {
|
||||||
|
// new MenuItem ("_Copy", "", null),
|
||||||
|
// new MenuItem ("C_ut", "", null),
|
||||||
|
// new MenuItem ("_Paste", "", null)
|
||||||
|
//})
|
||||||
|
});
|
||||||
|
top.Add(menu);
|
||||||
|
|
||||||
|
static bool Quit()
|
||||||
|
{
|
||||||
|
var n = MessageBox.Query(50, 7, "Quit BSL Manager", "Are you sure you want to quit?", "Yes", "No");
|
||||||
|
return n == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetrieveUserList();
|
||||||
|
|
||||||
|
var list = new ListView(_state.GetDisplayableList())
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 3,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = Dim.Fill()
|
||||||
|
};
|
||||||
|
|
||||||
|
list.KeyDown += _ =>
|
||||||
|
{
|
||||||
|
if (_.KeyEvent.Key == Key.Enter)
|
||||||
|
{
|
||||||
|
OpenFollowerDialog(list.SelectedItem);
|
||||||
|
}
|
||||||
|
else if (_.KeyEvent.Key == Key.Delete
|
||||||
|
|| _.KeyEvent.Key == Key.DeleteChar
|
||||||
|
|| _.KeyEvent.Key == Key.Backspace
|
||||||
|
|| _.KeyEvent.Key == Key.D)
|
||||||
|
{
|
||||||
|
OpenDeleteDialog(list.SelectedItem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listingFollowersLabel = new Label(1, 0, "Listing followers");
|
||||||
|
var filterLabel = new Label("Filter: ") { X = 1, Y = 1 };
|
||||||
|
var filterText = new TextField("")
|
||||||
|
{
|
||||||
|
X = Pos.Right(filterLabel),
|
||||||
|
Y = 1,
|
||||||
|
Width = 40
|
||||||
|
};
|
||||||
|
|
||||||
|
filterText.KeyDown += _ =>
|
||||||
|
{
|
||||||
|
var text = filterText.Text.ToString();
|
||||||
|
if (_.KeyEvent.Key == Key.Enter && !string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
_state.FilterBy(text);
|
||||||
|
ConsoleGui.RefreshUI();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win.Add(
|
||||||
|
listingFollowersLabel,
|
||||||
|
filterLabel,
|
||||||
|
filterText,
|
||||||
|
list
|
||||||
|
);
|
||||||
|
|
||||||
|
Application.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenFollowerDialog(int selectedIndex)
|
||||||
|
{
|
||||||
|
var close = new Button(3, 14, "Close");
|
||||||
|
close.Clicked += () => Application.RequestStop();
|
||||||
|
|
||||||
|
var dialog = new Dialog("Info", 60, 18, close);
|
||||||
|
|
||||||
|
var follower = _state.GetElementAt(selectedIndex);
|
||||||
|
|
||||||
|
var name = new Label($"User: @{follower.Acct}@{follower.Host}")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 1,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
var following = new Label($"Following Count: {follower.Followings.Count}")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 3,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
var inbox = new Label($"Inbox: {follower.InboxRoute}")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 4,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
var sharedInbox = new Label($"Shared Inbox: {follower.SharedInboxRoute}")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 5,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.Add(name);
|
||||||
|
dialog.Add(following);
|
||||||
|
dialog.Add(inbox);
|
||||||
|
dialog.Add(sharedInbox);
|
||||||
|
dialog.Add(close);
|
||||||
|
Application.Run(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenDeleteDialog(int selectedIndex)
|
||||||
|
{
|
||||||
|
bool okpressed = false;
|
||||||
|
var ok = new Button(10, 14, "Yes");
|
||||||
|
ok.Clicked += () =>
|
||||||
|
{
|
||||||
|
Application.RequestStop();
|
||||||
|
okpressed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var cancel = new Button(3, 14, "No");
|
||||||
|
cancel.Clicked += () => Application.RequestStop();
|
||||||
|
|
||||||
|
var dialog = new Dialog("Delete", 60, 18, cancel, ok);
|
||||||
|
|
||||||
|
var follower = _state.GetElementAt(selectedIndex);
|
||||||
|
var name = new Label($"User: @{follower.Acct}@{follower.Host}")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 1,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
var entry = new Label("Delete user and remove all their followings?")
|
||||||
|
{
|
||||||
|
X = 1,
|
||||||
|
Y = 3,
|
||||||
|
Width = Dim.Fill(),
|
||||||
|
Height = 1
|
||||||
|
};
|
||||||
|
dialog.Add(name);
|
||||||
|
dialog.Add(entry);
|
||||||
|
Application.Run(dialog);
|
||||||
|
|
||||||
|
if (okpressed)
|
||||||
|
{
|
||||||
|
DeleteAndRemoveUser(selectedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteAndRemoveUser(int el)
|
||||||
|
{
|
||||||
|
Application.MainLoop.Invoke(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userToDelete = _state.GetElementAt(el);
|
||||||
|
|
||||||
|
BasicLogger.Log($"Delete {userToDelete.Acct}@{userToDelete.Host}");
|
||||||
|
await _removeFollowerAction.ProcessAsync(userToDelete);
|
||||||
|
BasicLogger.Log($"Remove user from list");
|
||||||
|
_state.RemoveAt(el);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
BasicLogger.Log(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleGui.RefreshUI();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RetrieveUserList()
|
||||||
|
{
|
||||||
|
Application.MainLoop.Invoke(async () =>
|
||||||
|
{
|
||||||
|
var followers = await _followersDal.GetAllFollowersAsync();
|
||||||
|
_state.Load(followers.ToList());
|
||||||
|
ConsoleGui.RefreshUI();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
@ -6,7 +6,23 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lamar" Version="5.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||||
<PackageReference Include="Terminal.Gui" Version="1.0.0-beta.11" />
|
<PackageReference Include="Terminal.Gui" Version="1.0.0-beta.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\BirdsiteLive.Common\BirdsiteLive.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\BirdsiteLive.Moderation\BirdsiteLive.Moderation.csproj" />
|
||||||
|
<ProjectReference Include="..\DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="key.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
94
src/BSLManager/Bootstrapper.cs
Normal file
94
src/BSLManager/Bootstrapper.cs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using BirdsiteLive.Common.Settings;
|
||||||
|
using BirdsiteLive.Common.Structs;
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
using BirdsiteLive.DAL.Postgres.DataAccessLayers;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Settings;
|
||||||
|
using Lamar;
|
||||||
|
using Lamar.Scanning.Conventions;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace BSLManager
|
||||||
|
{
|
||||||
|
public class Bootstrapper
|
||||||
|
{
|
||||||
|
private readonly DbSettings _dbSettings;
|
||||||
|
private readonly InstanceSettings _instanceSettings;
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public Bootstrapper(DbSettings dbSettings, InstanceSettings instanceSettings)
|
||||||
|
{
|
||||||
|
_dbSettings = dbSettings;
|
||||||
|
_instanceSettings = instanceSettings;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public Container Init()
|
||||||
|
{
|
||||||
|
var container = new Container(x =>
|
||||||
|
{
|
||||||
|
x.For<DbSettings>().Use(x => _dbSettings);
|
||||||
|
|
||||||
|
x.For<InstanceSettings>().Use(x => _instanceSettings);
|
||||||
|
|
||||||
|
if (string.Equals(_dbSettings.Type, DbTypes.Postgres, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var connString = $"Host={_dbSettings.Host};Username={_dbSettings.User};Password={_dbSettings.Password};Database={_dbSettings.Name}";
|
||||||
|
var postgresSettings = new PostgresSettings
|
||||||
|
{
|
||||||
|
ConnString = connString
|
||||||
|
};
|
||||||
|
x.For<PostgresSettings>().Use(x => postgresSettings);
|
||||||
|
|
||||||
|
x.For<ITwitterUserDal>().Use<TwitterUserPostgresDal>().Singleton();
|
||||||
|
x.For<IFollowersDal>().Use<FollowersPostgresDal>().Singleton();
|
||||||
|
x.For<IDbInitializerDal>().Use<DbInitializerPostgresDal>().Singleton();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"{_dbSettings.Type} is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
|
||||||
|
x.For<IHttpClientFactory>().Use(_ => serviceProvider.GetService<IHttpClientFactory>());
|
||||||
|
|
||||||
|
x.For(typeof(ILogger<>)).Use(typeof(DummyLogger<>));
|
||||||
|
|
||||||
|
x.Scan(_ =>
|
||||||
|
{
|
||||||
|
_.Assembly("BirdsiteLive.Twitter");
|
||||||
|
_.Assembly("BirdsiteLive.Domain");
|
||||||
|
_.Assembly("BirdsiteLive.DAL");
|
||||||
|
_.Assembly("BirdsiteLive.DAL.Postgres");
|
||||||
|
_.Assembly("BirdsiteLive.Moderation");
|
||||||
|
|
||||||
|
_.TheCallingAssembly();
|
||||||
|
|
||||||
|
_.WithDefaultConventions();
|
||||||
|
|
||||||
|
_.LookForRegistries();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DummyLogger<T> : ILogger<T>
|
||||||
|
{
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
src/BSLManager/Domain/FollowersListState.cs
Normal file
81
src/BSLManager/Domain/FollowersListState.cs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BirdsiteLive.DAL.Models;
|
||||||
|
|
||||||
|
namespace BSLManager.Domain
|
||||||
|
{
|
||||||
|
public class FollowersListState
|
||||||
|
{
|
||||||
|
private readonly List<string> _filteredDisplayableUserList = new List<string>();
|
||||||
|
|
||||||
|
private List<Follower> _sourceUserList = new List<Follower>();
|
||||||
|
private List<Follower> _filteredSourceUserList = new List<Follower>();
|
||||||
|
|
||||||
|
public void Load(List<Follower> followers)
|
||||||
|
{
|
||||||
|
_sourceUserList = followers.OrderByDescending(x => x.Followings.Count).ToList();
|
||||||
|
|
||||||
|
ResetLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetLists()
|
||||||
|
{
|
||||||
|
_filteredSourceUserList = _sourceUserList.ToList();
|
||||||
|
|
||||||
|
_filteredDisplayableUserList.Clear();
|
||||||
|
|
||||||
|
foreach (var follower in _sourceUserList)
|
||||||
|
{
|
||||||
|
var displayedUser = $"{GetFullHandle(follower)} ({follower.Followings.Count})";
|
||||||
|
_filteredDisplayableUserList.Add(displayedUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetDisplayableList()
|
||||||
|
{
|
||||||
|
return _filteredDisplayableUserList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FilterBy(string pattern)
|
||||||
|
{
|
||||||
|
ResetLists();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(pattern))
|
||||||
|
{
|
||||||
|
var elToRemove = _filteredSourceUserList
|
||||||
|
.Where(x => !GetFullHandle(x).Contains(pattern))
|
||||||
|
.Select(x => x)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var el in elToRemove)
|
||||||
|
{
|
||||||
|
_filteredSourceUserList.Remove(el);
|
||||||
|
|
||||||
|
var dElToRemove = _filteredDisplayableUserList.First(x => x.Contains(GetFullHandle(el)));
|
||||||
|
_filteredDisplayableUserList.Remove(dElToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFullHandle(Follower follower)
|
||||||
|
{
|
||||||
|
return $"@{follower.Acct}@{follower.Host}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
var displayableUser = _filteredDisplayableUserList[index];
|
||||||
|
var sourceUser = _filteredSourceUserList[index];
|
||||||
|
|
||||||
|
_filteredDisplayableUserList.Remove(displayableUser);
|
||||||
|
|
||||||
|
_filteredSourceUserList.Remove(sourceUser);
|
||||||
|
_sourceUserList.Remove(sourceUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Follower GetElementAt(int index)
|
||||||
|
{
|
||||||
|
return _filteredSourceUserList[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.Common.Settings;
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
using BSLManager.Tools;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using NStack;
|
using NStack;
|
||||||
using Terminal.Gui;
|
using Terminal.Gui;
|
||||||
|
|
||||||
|
@ -9,111 +15,25 @@ namespace BSLManager
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.OutputEncoding = Encoding.Default;
|
Console.OutputEncoding = Encoding.Default;
|
||||||
|
|
||||||
Application.Init();
|
var settingsManager = new SettingsManager();
|
||||||
var top = Application.Top;
|
var settings = settingsManager.GetSettings();
|
||||||
|
|
||||||
// Creates the top-level window to show
|
//var builder = new ConfigurationBuilder()
|
||||||
var win = new Window("BSL Manager")
|
// .AddEnvironmentVariables();
|
||||||
{
|
//var configuration = builder.Build();
|
||||||
X = 0,
|
|
||||||
Y = 1, // Leave one row for the toplevel menu
|
|
||||||
|
|
||||||
// By using Dim.Fill(), it will automatically resize without manual intervention
|
//var dbSettings = configuration.GetSection("Db").Get<DbSettings>();
|
||||||
Width = Dim.Fill(),
|
//var instanceSettings = configuration.GetSection("Instance").Get<InstanceSettings>();
|
||||||
Height = Dim.Fill()
|
|
||||||
};
|
|
||||||
|
|
||||||
top.Add(win);
|
var bootstrapper = new Bootstrapper(settings.dbSettings, settings.instanceSettings);
|
||||||
|
var container = bootstrapper.Init();
|
||||||
|
|
||||||
// Creates a menubar, the item "New" has a help menu.
|
var app = container.GetInstance<App>();
|
||||||
var menu = new MenuBar(new MenuBarItem[] {
|
app.Run();
|
||||||
new MenuBarItem ("_File", new MenuItem [] {
|
|
||||||
new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
|
|
||||||
}),
|
|
||||||
//new MenuBarItem ("_Edit", new MenuItem [] {
|
|
||||||
// new MenuItem ("_Copy", "", null),
|
|
||||||
// new MenuItem ("C_ut", "", null),
|
|
||||||
// new MenuItem ("_Paste", "", null)
|
|
||||||
//})
|
|
||||||
});
|
|
||||||
top.Add(menu);
|
|
||||||
|
|
||||||
static bool Quit()
|
|
||||||
{
|
|
||||||
var n = MessageBox.Query(50, 7, "Quit BSL Manager", "Are you sure you want to quit?", "Yes", "No");
|
|
||||||
return n == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var listData = new List<string>();
|
|
||||||
for (var i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
listData.Add($"@User{i}@Instance.tld {i*3}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = new ListView(listData)
|
|
||||||
{
|
|
||||||
X = 1,
|
|
||||||
Y = 2,
|
|
||||||
Width = Dim.Fill(),
|
|
||||||
Height = Dim.Fill()
|
|
||||||
};
|
|
||||||
|
|
||||||
list.KeyDown += _ =>
|
|
||||||
{
|
|
||||||
if (_.KeyEvent.Key == Key.Enter)
|
|
||||||
{
|
|
||||||
var el = list.SelectedItem;
|
|
||||||
|
|
||||||
bool okpressed = false;
|
|
||||||
var ok = new Button(10, 14, "Yes");
|
|
||||||
ok.Clicked += () =>
|
|
||||||
{
|
|
||||||
Application.RequestStop();
|
|
||||||
okpressed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var cancel = new Button(3, 14, "No");
|
|
||||||
cancel.Clicked += () => Application.RequestStop();
|
|
||||||
|
|
||||||
var dialog = new Dialog("Delete", 60, 18, cancel, ok);
|
|
||||||
|
|
||||||
var name = new Label($"User: {listData[el]}")
|
|
||||||
{
|
|
||||||
X = 1,
|
|
||||||
Y = 1,
|
|
||||||
Width = Dim.Fill(),
|
|
||||||
Height = 1
|
|
||||||
};
|
|
||||||
var entry = new Label("Delete user and remove all their followings?")
|
|
||||||
{
|
|
||||||
X = 1,
|
|
||||||
Y = 3,
|
|
||||||
Width = Dim.Fill(),
|
|
||||||
Height = 1
|
|
||||||
};
|
|
||||||
dialog.Add(name);
|
|
||||||
dialog.Add(entry);
|
|
||||||
Application.Run(dialog);
|
|
||||||
|
|
||||||
if (okpressed)
|
|
||||||
{
|
|
||||||
listData.RemoveAt(el);
|
|
||||||
typeof(Application).GetMethod("TerminalResized", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add some controls,
|
|
||||||
win.Add(
|
|
||||||
new Label(1, 0, "Listing followers"),
|
|
||||||
list
|
|
||||||
);
|
|
||||||
|
|
||||||
Application.Run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/BSLManager/Tools/BasicLogger.cs
Normal file
13
src/BSLManager/Tools/BasicLogger.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BSLManager.Tools
|
||||||
|
{
|
||||||
|
public static class BasicLogger
|
||||||
|
{
|
||||||
|
public static void Log(string log)
|
||||||
|
{
|
||||||
|
File.AppendAllLines($"Log-{Guid.NewGuid()}.txt", new []{ log });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/BSLManager/Tools/ConsoleGui.cs
Normal file
15
src/BSLManager/Tools/ConsoleGui.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using Terminal.Gui;
|
||||||
|
|
||||||
|
namespace BSLManager.Tools
|
||||||
|
{
|
||||||
|
public static class ConsoleGui
|
||||||
|
{
|
||||||
|
public static void RefreshUI()
|
||||||
|
{
|
||||||
|
typeof(Application)
|
||||||
|
.GetMethod("TerminalResized", BindingFlags.Static | BindingFlags.NonPublic)
|
||||||
|
.Invoke(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
src/BSLManager/Tools/SettingsManager.cs
Normal file
124
src/BSLManager/Tools/SettingsManager.cs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using BirdsiteLive.Common.Settings;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Org.BouncyCastle.Asn1.IsisMtt.X509;
|
||||||
|
|
||||||
|
namespace BSLManager.Tools
|
||||||
|
{
|
||||||
|
public class SettingsManager
|
||||||
|
{
|
||||||
|
private const string LocalFileName = "ManagerSettings.json";
|
||||||
|
|
||||||
|
public (DbSettings dbSettings, InstanceSettings instanceSettings) GetSettings()
|
||||||
|
{
|
||||||
|
var localSettingsData = GetLocalSettingsFile();
|
||||||
|
if (localSettingsData != null) return Convert(localSettingsData);
|
||||||
|
|
||||||
|
Console.WriteLine("We need to set up the manager");
|
||||||
|
Console.WriteLine("Please provide the following information as provided in the docker-compose file");
|
||||||
|
|
||||||
|
LocalSettingsData data;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
data = GetDataFromUser();
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Please check if all is ok:");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine($"Db Host: {data.DbHost}");
|
||||||
|
Console.WriteLine($"Db Name: {data.DbName}");
|
||||||
|
Console.WriteLine($"Db User: {data.DbUser}");
|
||||||
|
Console.WriteLine($"Db Password: {data.DbPassword}");
|
||||||
|
Console.WriteLine($"Instance Domain: {data.InstanceDomain}");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
string resp;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Console.WriteLine("Is it valid? (yes, no)");
|
||||||
|
resp = Console.ReadLine()?.Trim().ToLowerInvariant();
|
||||||
|
|
||||||
|
if (resp == "n" || resp == "no") data = null;
|
||||||
|
|
||||||
|
} while (resp != "y" && resp != "yes" && resp != "n" && resp != "no");
|
||||||
|
|
||||||
|
} while (data == null);
|
||||||
|
|
||||||
|
SaveLocalSettings(data);
|
||||||
|
return Convert(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalSettingsData GetDataFromUser()
|
||||||
|
{
|
||||||
|
var data = new LocalSettingsData();
|
||||||
|
|
||||||
|
Console.WriteLine("Db Host:");
|
||||||
|
data.DbHost = Console.ReadLine();
|
||||||
|
|
||||||
|
Console.WriteLine("Db Name:");
|
||||||
|
data.DbName = Console.ReadLine();
|
||||||
|
|
||||||
|
Console.WriteLine("Db User:");
|
||||||
|
data.DbUser = Console.ReadLine();
|
||||||
|
|
||||||
|
Console.WriteLine("Db Password:");
|
||||||
|
data.DbPassword = Console.ReadLine();
|
||||||
|
|
||||||
|
Console.WriteLine("Instance Domain:");
|
||||||
|
data.InstanceDomain = Console.ReadLine();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (DbSettings dbSettings, InstanceSettings instanceSettings) Convert(LocalSettingsData data)
|
||||||
|
{
|
||||||
|
var dbSettings = new DbSettings
|
||||||
|
{
|
||||||
|
Type = data.DbType,
|
||||||
|
Host = data.DbHost,
|
||||||
|
Name = data.DbName,
|
||||||
|
User = data.DbUser,
|
||||||
|
Password = data.DbPassword
|
||||||
|
};
|
||||||
|
var instancesSettings = new InstanceSettings
|
||||||
|
{
|
||||||
|
Domain = data.InstanceDomain
|
||||||
|
};
|
||||||
|
return (dbSettings, instancesSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalSettingsData GetLocalSettingsFile()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(LocalFileName)) return null;
|
||||||
|
|
||||||
|
var jsonContent = File.ReadAllText(LocalFileName);
|
||||||
|
var content = JsonConvert.DeserializeObject<LocalSettingsData>(jsonContent);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveLocalSettings(LocalSettingsData data)
|
||||||
|
{
|
||||||
|
var jsonContent = JsonConvert.SerializeObject(data);
|
||||||
|
File.WriteAllText(LocalFileName, jsonContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LocalSettingsData
|
||||||
|
{
|
||||||
|
public string DbType { get; set; } = "postgres";
|
||||||
|
public string DbHost { get; set; }
|
||||||
|
public string DbName { get; set; }
|
||||||
|
public string DbUser { get; set; }
|
||||||
|
public string DbPassword { get; set; }
|
||||||
|
|
||||||
|
public string InstanceDomain { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Threading.Tasks;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using BirdsiteLive.DAL.Contracts;
|
using BirdsiteLive.DAL.Contracts;
|
||||||
using BirdsiteLive.Domain.Repository;
|
using BirdsiteLive.Domain.Repository;
|
||||||
using BirdsiteLive.Moderation.Actions;
|
using BirdsiteLive.Moderation.Actions;
|
||||||
|
@ -38,8 +39,11 @@ namespace BirdsiteLive.Moderation.Processors
|
||||||
|
|
||||||
if (type == ModerationTypeEnum.WhiteListing && status != ModeratedTypeEnum.WhiteListed ||
|
if (type == ModerationTypeEnum.WhiteListing && status != ModeratedTypeEnum.WhiteListed ||
|
||||||
type == ModerationTypeEnum.BlackListing && status == ModeratedTypeEnum.BlackListed)
|
type == ModerationTypeEnum.BlackListing && status == ModeratedTypeEnum.BlackListed)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Remove {followerHandle}");
|
||||||
await _removeFollowerAction.ProcessAsync(follower);
|
await _removeFollowerAction.ProcessAsync(follower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
|
@ -47,7 +47,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Moderation.Tes
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Common.Tests", "Tests\BirdsiteLive.Common.Tests\BirdsiteLive.Common.Tests.csproj", "{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Common.Tests", "Tests\BirdsiteLive.Common.Tests\BirdsiteLive.Common.Tests.csproj", "{C69F7582-6050-44DC-BAAB-7C8F0BDA525C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSLManager", "BSLManager\BSLManager.csproj", "{4A84D351-E91B-4E58-8E20-211F0F4991D7}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BSLManager", "BSLManager\BSLManager.csproj", "{4A84D351-E91B-4E58-8E20-211F0F4991D7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSLManager.Tests", "Tests\BSLManager.Tests\BSLManager.Tests.csproj", "{D4457271-620E-465A-B08E-7FC63C99A2F6}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -131,6 +133,10 @@ Global
|
||||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4A84D351-E91B-4E58-8E20-211F0F4991D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D4457271-620E-465A-B08E-7FC63C99A2F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -153,6 +159,7 @@ Global
|
||||||
{4BE541AC-8A93-4FA3-98AC-956CC2D5B748} = {DA3C160C-4811-4E26-A5AD-42B81FAF2D7C}
|
{4BE541AC-8A93-4FA3-98AC-956CC2D5B748} = {DA3C160C-4811-4E26-A5AD-42B81FAF2D7C}
|
||||||
{0A311BF3-4FD9-4303-940A-A3778890561C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
{0A311BF3-4FD9-4303-940A-A3778890561C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
{C69F7582-6050-44DC-BAAB-7C8F0BDA525C} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
|
{D4457271-620E-465A-B08E-7FC63C99A2F6} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
|
<UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<Version>0.16.2</Version>
|
<Version>0.17.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -5,12 +5,22 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace BirdsiteLive.Controllers
|
namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class InboxController : ControllerBase
|
public class InboxController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<InboxController> _logger;
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public InboxController(ILogger<InboxController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
[Route("/inbox")]
|
[Route("/inbox")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Inbox()
|
public async Task<IActionResult> Inbox()
|
||||||
|
@ -19,6 +29,8 @@ namespace BirdsiteLive.Controllers
|
||||||
using (var reader = new StreamReader(Request.Body))
|
using (var reader = new StreamReader(Request.Body))
|
||||||
{
|
{
|
||||||
var body = await reader.ReadToEndAsync();
|
var body = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
_logger.LogTrace("Inbox: {Body}", body);
|
||||||
//System.IO.File.WriteAllText($@"C:\apdebug\inbox\{Guid.NewGuid()}.json", body);
|
//System.IO.File.WriteAllText($@"C:\apdebug\inbox\{Guid.NewGuid()}.json", body);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ using BirdsiteLive.Twitter;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
@ -29,15 +30,17 @@ namespace BirdsiteLive.Controllers
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IStatusService _statusService;
|
private readonly IStatusService _statusService;
|
||||||
private readonly InstanceSettings _instanceSettings;
|
private readonly InstanceSettings _instanceSettings;
|
||||||
|
private readonly ILogger<UsersController> _logger;
|
||||||
|
|
||||||
#region Ctor
|
#region Ctor
|
||||||
public UsersController(ITwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ITwitterTweetsService twitterTweetService)
|
public UsersController(ITwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ITwitterTweetsService twitterTweetService, ILogger<UsersController> logger)
|
||||||
{
|
{
|
||||||
_twitterUserService = twitterUserService;
|
_twitterUserService = twitterUserService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_statusService = statusService;
|
_statusService = statusService;
|
||||||
_instanceSettings = instanceSettings;
|
_instanceSettings = instanceSettings;
|
||||||
_twitterTweetService = twitterTweetService;
|
_twitterTweetService = twitterTweetService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -57,6 +60,8 @@ namespace BirdsiteLive.Controllers
|
||||||
[Route("/users/{id}")]
|
[Route("/users/{id}")]
|
||||||
public IActionResult Index(string id)
|
public IActionResult Index(string id)
|
||||||
{
|
{
|
||||||
|
_logger.LogTrace("User Index: {Id}", id);
|
||||||
|
|
||||||
id = id.Trim(new[] { ' ', '@' }).ToLowerInvariant();
|
id = id.Trim(new[] { ' ', '@' }).ToLowerInvariant();
|
||||||
|
|
||||||
// Ensure valid username
|
// Ensure valid username
|
||||||
|
@ -131,6 +136,8 @@ namespace BirdsiteLive.Controllers
|
||||||
using (var reader = new StreamReader(Request.Body))
|
using (var reader = new StreamReader(Request.Body))
|
||||||
{
|
{
|
||||||
var body = await reader.ReadToEndAsync();
|
var body = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
_logger.LogTrace("User Inbox: {Body}", body);
|
||||||
//System.IO.File.WriteAllText($@"C:\apdebug\{Guid.NewGuid()}.json", body);
|
//System.IO.File.WriteAllText($@"C:\apdebug\{Guid.NewGuid()}.json", body);
|
||||||
|
|
||||||
var activity = ApDeserializer.ProcessActivity(body);
|
var activity = ApDeserializer.ProcessActivity(body);
|
||||||
|
|
20
src/Tests/BSLManager.Tests/BSLManager.Tests.csproj
Normal file
20
src/Tests/BSLManager.Tests/BSLManager.Tests.csproj
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\BSLManager\BSLManager.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
307
src/Tests/BSLManager.Tests/Domain/FollowersListStateTests.cs
Normal file
307
src/Tests/BSLManager.Tests/Domain/FollowersListStateTests.cs
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BirdsiteLive.DAL.Models;
|
||||||
|
using BSLManager.Domain;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace BSLManager.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class FollowersListStateTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void FilterBy()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers);
|
||||||
|
|
||||||
|
state.FilterBy("test");
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
Assert.AreEqual(2, state.GetDisplayableList().Count);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void FilterBy_GetElement()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers);
|
||||||
|
|
||||||
|
state.FilterBy("test");
|
||||||
|
var el = state.GetElementAt(1);
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
Assert.AreEqual(followers[1].Id, el.Id);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void GetElement()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers);
|
||||||
|
|
||||||
|
var el = state.GetElementAt(2);
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
Assert.AreEqual(followers[2].Id, el.Id);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void FilterBy_RemoveAt()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers.ToList());
|
||||||
|
|
||||||
|
state.FilterBy("test");
|
||||||
|
state.RemoveAt(1);
|
||||||
|
|
||||||
|
var list = state.GetDisplayableList();
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
Assert.AreEqual(1, list.Count);
|
||||||
|
Assert.IsTrue(list[0].Contains("@test@host1"));
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void RemoveAt()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers.ToList());
|
||||||
|
|
||||||
|
state.RemoveAt(1);
|
||||||
|
|
||||||
|
var list = state.GetDisplayableList();
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
Assert.AreEqual(3, list.Count);
|
||||||
|
Assert.IsTrue(list[0].Contains("@test@host1"));
|
||||||
|
Assert.IsFalse(list[1].Contains("@test@host2"));
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void FilterBy_ResetFilter()
|
||||||
|
{
|
||||||
|
#region Stub
|
||||||
|
var followers = new List<Follower>
|
||||||
|
{
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Acct = "test",
|
||||||
|
Host = "host2",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Acct = "user1",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
},
|
||||||
|
new Follower
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Acct = "user2",
|
||||||
|
Host = "host1",
|
||||||
|
Followings = new List<int>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var state = new FollowersListState();
|
||||||
|
state.Load(followers.ToList());
|
||||||
|
|
||||||
|
#region Validate
|
||||||
|
state.FilterBy("data");
|
||||||
|
var list = state.GetDisplayableList();
|
||||||
|
Assert.AreEqual(0, list.Count);
|
||||||
|
|
||||||
|
state.FilterBy(string.Empty);
|
||||||
|
list = state.GetDisplayableList();
|
||||||
|
Assert.AreEqual(4, list.Count);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue