726 lines
27 KiB
C#
726 lines
27 KiB
C#
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using Newtonsoft.Json;
|
|
using System.Net.Http.Json;
|
|
using ttrss_co_client.ttrss.messages;
|
|
using ttrss_co_client.ttrss.datastructures;
|
|
|
|
// https://tt-rss.org/wiki/ApiReference
|
|
//TODO: a lot of these are a given at API level 1, so double check all necessary API levels
|
|
|
|
namespace ttrss_co_client.ttrss
|
|
{
|
|
public class ApiClient
|
|
{
|
|
public Uri BaseURI { get; private set; }
|
|
private HttpClient httpClient { get; set; }
|
|
private string SessionId { get; set; } = null;
|
|
private int api_level { get; set; }
|
|
|
|
public ApiClient(Uri baseUri)
|
|
{
|
|
BaseURI = baseUri;
|
|
httpClient = new HttpClient() { BaseAddress = baseUri };
|
|
}
|
|
|
|
public async Task Login(string username, string password)
|
|
{
|
|
var json = new
|
|
{
|
|
op = "login",
|
|
user = username,
|
|
password = password
|
|
};
|
|
var content = JsonContent.Create(json);
|
|
var response = await (await httpClient.PostAsync(BaseURI, content)).Content.ReadAsStringAsync();
|
|
var loginResult = JsonConvert.DeserializeObject<ApiResponse<LoginResponseContent>>(response);
|
|
if (loginResult.status == 0)
|
|
{
|
|
SessionId = loginResult.Content.session_id;
|
|
if (loginResult.Content.api_level == null)
|
|
{
|
|
throw new NotImplementedException($"api doesn't report an api level - unsupported. api level 1 is version 1.5.8, so {BaseURI} might be extremely old. (or maybe this library is extremely old and ttrss changed again?)");
|
|
}
|
|
else
|
|
{
|
|
api_level = loginResult.Content.api_level.Value;
|
|
}
|
|
|
|
Console.WriteLine(SessionId);
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
public async Task<int> GetApiLevel()
|
|
{
|
|
//1.5.8 = api level 1, any lower than that, unsupported.
|
|
//???? = 2
|
|
//???? = 3
|
|
//1.6.0 = 4
|
|
//1.7.6 = 5
|
|
//1.8 = ????
|
|
//???? = 6
|
|
//???? = 7
|
|
//???? = 8
|
|
//1.14 = 9
|
|
//1.15 = 10
|
|
assertInitialized();
|
|
return await getOneValue<int>("getApiLevel", "level");
|
|
}
|
|
|
|
public async Task<bool> Logout()
|
|
{
|
|
assertInitialized();
|
|
|
|
return (await getOneValue<string>("logout", "status"))?.ToLower() == "ok";
|
|
}
|
|
public async Task<bool> IsLoggedIn()
|
|
{
|
|
//assertInitialized();
|
|
return (await getOneValue<bool>("isLoggedIn", "status"));
|
|
}
|
|
public async Task<int> GetUnread()
|
|
{
|
|
assertInitialized();
|
|
return await getOneValue<int>("getUnread", "unread");
|
|
}
|
|
|
|
///<summary>at least in my installation, it doesn't seem to respect the parameters I give it, be it curl or here.</summary>
|
|
public async Task<IEnumerable<CounterInfo>> GetCounters(bool feeds = true, bool labels = true, bool categories = true, bool tags = false)
|
|
{
|
|
assertInitialized();
|
|
|
|
var output_mode = "";
|
|
if (feeds) output_mode += "f";
|
|
if (labels) output_mode += "l";
|
|
if (categories) output_mode += "c";
|
|
if (tags) output_mode += "t";
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getCounters",
|
|
sid = this.SessionId,
|
|
output_mode = output_mode
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<IEnumerable<CounterInfo>>>(response);
|
|
return apiResult.Content;
|
|
}
|
|
///<param name="limit">0 for all</param>
|
|
///<param name="offset">skip this amount first</param>
|
|
///<param name="include_nested">idk, doesn't affect what the documentation says it should</param>
|
|
public async Task<IEnumerable<Feed>> GetFeeds(int cat_id = 0, bool unread_only = false, uint limit = 0, int offset = 0/*, bool include_nested*/)
|
|
{
|
|
assertInitialized();
|
|
if (cat_id < -2)
|
|
{
|
|
if (cat_id == -3 || cat_id == -4)
|
|
{
|
|
assertApiLevel(4);
|
|
}
|
|
else
|
|
{
|
|
throw new IndexOutOfRangeException($"cat_id {cat_id} is out of range");
|
|
}
|
|
}
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getFeeds",
|
|
sid = this.SessionId,
|
|
cat_id = cat_id,
|
|
unread_only = unread_only,
|
|
limit = limit,
|
|
offset = offset
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<IEnumerable<Feed>>>(response);
|
|
return apiResult.Content;
|
|
}
|
|
///<param name="enable_nested">nested mode (return only top level)</param>
|
|
public async Task<IEnumerable<Category>> GetCategories(bool unread_only = false, bool enable_nested = false, bool include_empty = false)
|
|
{
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getCategories",
|
|
sid = this.SessionId,
|
|
unread_only = unread_only,
|
|
enable_nested = enable_nested,
|
|
include_empty = include_empty
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<IEnumerable<Category>>>(response);
|
|
return apiResult.Content;
|
|
}
|
|
public enum VIEWMODE { All, Unread, Adaptive, Marked, Updated }
|
|
public enum SORTORDER { Default, OldestFirst, NewestFirst }
|
|
public async Task<IEnumerable<Headline>> GetHeadlines(
|
|
int feed_id,
|
|
bool is_cat,
|
|
int limit = 60,
|
|
int skip = 0,
|
|
/*string filter,*/
|
|
bool show_excerpt = false,
|
|
bool show_content = false,
|
|
VIEWMODE view_mode = VIEWMODE.All,
|
|
bool include_attachments = false,
|
|
int? since_id = null,
|
|
bool include_nested = false,
|
|
SORTORDER order_by = SORTORDER.Default,
|
|
bool sanitize = true,
|
|
bool force_update = false,
|
|
bool has_sandbox = false)
|
|
{
|
|
if (limit > 60)
|
|
{
|
|
assertApiLevel(6);
|
|
}
|
|
if (limit > 200)
|
|
{
|
|
throw new ArgumentOutOfRangeException("limit", limit, "capped at 200");
|
|
}
|
|
|
|
if (include_nested)
|
|
{
|
|
assertApiLevel(4);
|
|
}
|
|
|
|
string sortOrderString = "";
|
|
if (order_by != SORTORDER.Default)
|
|
{
|
|
assertApiLevel(5);
|
|
switch (order_by)
|
|
{
|
|
case SORTORDER.OldestFirst:
|
|
sortOrderString = "date_reverse";
|
|
break;
|
|
case SORTORDER.NewestFirst:
|
|
sortOrderString = "feed_dates";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sanitize == false)
|
|
{
|
|
//TODO: it's version 1.8.0, but no idea what version that is. I can narrow it down to 6, 7, or 8.
|
|
assertApiLevel(6);
|
|
}
|
|
if (force_update)
|
|
{
|
|
assertApiLevel(9);
|
|
}
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getHeadlines",
|
|
sid = this.SessionId,
|
|
feed_id = feed_id,
|
|
is_cat = is_cat,
|
|
limit = limit,
|
|
skip = skip,
|
|
show_excerpt = show_excerpt,
|
|
show_content = show_content,
|
|
view_mode = view_mode.ToString("D"),
|
|
include_attachments = include_attachments,
|
|
since_id = since_id,
|
|
include_nested = include_nested,
|
|
order_by = sortOrderString,
|
|
sanitize = sanitize,
|
|
force_update = force_update,
|
|
has_sandbox = has_sandbox
|
|
});
|
|
return await getHeadlines(json);
|
|
}
|
|
public async Task<IEnumerable<Headline>> GetHeadlinesTag(
|
|
string tag,
|
|
int limit = 200,
|
|
int skip = 0,
|
|
/*string filter,*/
|
|
bool show_excerpt = false,
|
|
bool show_content = false,
|
|
VIEWMODE view_mode = VIEWMODE.All,
|
|
bool include_attachments = false,
|
|
int? since_id = null,
|
|
bool include_nested = false,
|
|
SORTORDER order_by = SORTORDER.Default,
|
|
bool sanitize = true,
|
|
bool force_update = false,
|
|
bool has_sandbox = false
|
|
/*bool include_header = false*/)
|
|
{
|
|
assertApiLevel(18);
|
|
|
|
if (limit > 200)
|
|
{
|
|
throw new ArgumentOutOfRangeException("limit", limit, "capped at 200");
|
|
}
|
|
|
|
string sortOrderString;
|
|
switch (order_by)
|
|
{
|
|
case SORTORDER.OldestFirst:
|
|
sortOrderString = "date_reverse";
|
|
break;
|
|
case SORTORDER.NewestFirst:
|
|
sortOrderString = "feed_dates";
|
|
break;
|
|
default:
|
|
sortOrderString = "";
|
|
break;
|
|
}
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getHeadlines",
|
|
sid = this.SessionId,
|
|
feed_id = tag,
|
|
limit = limit,
|
|
skip = skip,
|
|
show_excerpt = show_excerpt,
|
|
show_content = show_content,
|
|
view_mode = view_mode.ToString("D"),
|
|
include_attachments = include_attachments,
|
|
since_id = since_id,
|
|
include_nested = include_nested,
|
|
order_by = sortOrderString,
|
|
sanitize = sanitize,
|
|
force_update = force_update,
|
|
has_sandbox = has_sandbox
|
|
});
|
|
return await getHeadlines(json);
|
|
}
|
|
private async Task<IEnumerable<Headline>> getHeadlines(JsonContent parameters)
|
|
{
|
|
var response = await (await httpClient.PostAsync(BaseURI, parameters)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<IEnumerable<Headline>>>(response);
|
|
return apiResult.Content;
|
|
}
|
|
#region Get headlines include_header=true
|
|
// public async Task<Tuple<ttrss.messages.HeadlinesHeaderResponse, IEnumerable<Headline>>> GetHeadlinesAndHeader(
|
|
// int feed_id,
|
|
// bool is_cat,
|
|
// int limit=60,
|
|
// int skip=0,
|
|
// /*string filter,*/
|
|
// bool show_excerpt = false,
|
|
// bool show_content=false,
|
|
// VIEWMODE view_mode = VIEWMODE.All,
|
|
// bool include_attachments = false,
|
|
// int? since_id = null,
|
|
// bool include_nested = false,
|
|
// SORTORDER order_by = SORTORDER.Default,
|
|
// bool sanitize = true,
|
|
// bool force_update = false,
|
|
// bool has_sandbox = false)
|
|
// {
|
|
// assertApiLevel(12);
|
|
// if(limit > 200)
|
|
// {
|
|
// throw new ArgumentOutOfRangeException("limit", limit, "capped at 200");
|
|
// }
|
|
|
|
// string sortOrderString = "";
|
|
// if (order_by != SORTORDER.Default)
|
|
// {
|
|
// switch (order_by)
|
|
// {
|
|
// case SORTORDER.OldestFirst:
|
|
// sortOrderString = "date_reverse";
|
|
// break;
|
|
// case SORTORDER.NewestFirst:
|
|
// sortOrderString = "feed_dates";
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// var json = JsonContent.Create(new
|
|
// {
|
|
// op = "getHeadlines",
|
|
// sid = this.SessionId,
|
|
// feed_id = feed_id,
|
|
// is_cat = is_cat,
|
|
// limit = limit,
|
|
// skip = skip,
|
|
// show_excerpt = show_excerpt,
|
|
// show_content = show_content,
|
|
// view_mode = view_mode.ToString("D"),
|
|
// include_attachments = include_attachments,
|
|
// since_id = since_id,
|
|
// include_nested = include_nested,
|
|
// order_by = sortOrderString,
|
|
// sanitize = sanitize,
|
|
// force_update = force_update,
|
|
// has_sandbox = has_sandbox,
|
|
// include_header = true
|
|
// });
|
|
// return await getHeadlinesAndHeader(json);
|
|
// }
|
|
// public async Task<HeadlinesHeaderResponse> GetHeadlinesTagAndHeader(
|
|
// string tag,
|
|
// int limit=200,
|
|
// int skip=0,
|
|
// /*string filter,*/
|
|
// bool show_excerpt = false,
|
|
// bool show_content=false,
|
|
// VIEWMODE view_mode = VIEWMODE.All,
|
|
// bool include_attachments = false,
|
|
// int? since_id = null,
|
|
// bool include_nested = false,
|
|
// SORTORDER order_by = SORTORDER.Default,
|
|
// bool sanitize = true,
|
|
// bool force_update = false,
|
|
// bool has_sandbox = false
|
|
// /*bool include_header = false*/)
|
|
// {
|
|
// assertApiLevel(18);
|
|
|
|
// if(limit > 200)
|
|
// {
|
|
// throw new ArgumentOutOfRangeException("limit", limit, "capped at 200");
|
|
// }
|
|
|
|
// string sortOrderString;
|
|
// switch(order_by)
|
|
// {
|
|
// case SORTORDER.OldestFirst:
|
|
// sortOrderString = "date_reverse";
|
|
// break;
|
|
// case SORTORDER.NewestFirst:
|
|
// sortOrderString = "feed_dates";
|
|
// break;
|
|
// default:
|
|
// sortOrderString = "";
|
|
// break;
|
|
// }
|
|
|
|
// var json = JsonContent.Create(new
|
|
// {
|
|
// op = "getHeadlines",
|
|
// sid = this.SessionId,
|
|
// feed_id = tag,
|
|
// limit = limit,
|
|
// skip = skip,
|
|
// show_excerpt = show_excerpt,
|
|
// show_content = show_content,
|
|
// view_mode = view_mode.ToString("D"),
|
|
// include_attachments = include_attachments,
|
|
// since_id = since_id,
|
|
// include_nested = include_nested,
|
|
// order_by = sortOrderString,
|
|
// sanitize = sanitize,
|
|
// force_update = force_update,
|
|
// has_sandbox = has_sandbox,
|
|
// include_header = true
|
|
// });
|
|
// return await getHeadlinesAndHeader(json);
|
|
// }
|
|
|
|
//todo: deserialize array of disparate types. can that be done?
|
|
// private async Task<Tuple<ttrss.messages.HeadlinesHeaderResponse, IEnumerable<Headline>>> getHeadlinesAndHeader(JsonContent parameters)
|
|
// {
|
|
// var response = await (await httpClient.PostAsync(BaseURI, parameters)).Content.ReadAsStringAsync();
|
|
// var immediateApiResult = JsonConvert.DeserializeObject<ApiResponse<IEnumerable<string>>>(response).Content.ToList();
|
|
// var apiResult = JsonConvert.DeserializeObject<Tuple<ttrss.messages.HeadlinesHeaderResponse, IEnumerable<Headline>>>(immediateApiResult[1]);
|
|
// return apiResult;
|
|
// }
|
|
#endregion
|
|
///<summary>to update note, see <see cref="UpdateArticleNote"/></summary>
|
|
public enum UPDATEFIELD { starred = 0, published = 1, unread = 2 }
|
|
public enum UPDATEMODE { SetFalse = 0, SetTrue = 1, Toggle = 2 }
|
|
public async Task<int> UpdateArticleField(UPDATEFIELD field, UPDATEMODE mode, params int[] ids)
|
|
{
|
|
if (ids == null || ids.Length == 0)
|
|
{
|
|
throw new System.ArgumentNullException("ids", "need to specify at least one id");
|
|
}
|
|
//documentation: UpdateArticle - for note, we have a separate method UpdateArticleNote
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "updateArticle",
|
|
sid = this.SessionId,
|
|
article_ids = string.Join(',', ids),
|
|
mode = (int)mode,
|
|
field = (int)field
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<ArticleUpdateFieldResponse>>(response);
|
|
return apiResult.Content.updated;
|
|
|
|
throw new NotImplementedException();
|
|
}
|
|
///<summary>for fields other than note, we have a separate method UpdateArticleField</summary>
|
|
public async Task<int> UpdateArticleNote(string data, params int[] ids)
|
|
{
|
|
if (ids == null || ids.Length == 0)
|
|
{
|
|
throw new System.ArgumentNullException("ids", "need to specify at least one id");
|
|
}
|
|
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "updateArticle",
|
|
sid = this.SessionId,
|
|
article_ids = string.Join(',', ids),
|
|
field = 3,
|
|
data = data,
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ApiResponse<ArticleUpdateFieldResponse>>(response);
|
|
return apiResult.Content.updated;
|
|
}
|
|
public async Task<IEnumerable<Article>> GetArticles(params int[] article_id)
|
|
{
|
|
if (!article_id.Any())
|
|
{
|
|
throw new ArgumentException("need at least one article_id");
|
|
}
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getArticle",
|
|
sid = this.SessionId,
|
|
article_id = string.Join(',', article_id)
|
|
});
|
|
return await get<IEnumerable<Article>>(json);
|
|
}
|
|
public async Task<datastructures.Configuration> GetConfig()
|
|
{
|
|
assertInitialized();
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getConfig",
|
|
sid = this.SessionId
|
|
});
|
|
return await get<datastructures.Configuration>(json);
|
|
}
|
|
///<summary>
|
|
///tell the feed to update. As opposed to updating our configuration of the feed.
|
|
///</summary>
|
|
public async Task<bool> UpdateFeed(int feed_id)
|
|
{
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "updateFeed",
|
|
sid = this.SessionId,
|
|
feed_id = feed_id
|
|
});
|
|
var apiResponse = await get<Dictionary<string, string>>(json);
|
|
|
|
return apiResponse.ContainsKey("status") && apiResponse["status"]?.ToLower() == "ok";
|
|
}
|
|
public async Task<T> GetPref<T>(string pref)
|
|
{
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getPref",
|
|
sid = this.SessionId,
|
|
pref_name = pref
|
|
});
|
|
var apiResponse = await get<Dictionary<string, string>>(json);
|
|
try
|
|
{
|
|
var converter = TypeDescriptor.GetConverter(typeof(T));
|
|
if (converter != null)
|
|
{
|
|
return (T)converter.ConvertFromString(apiResponse["value"]);
|
|
}
|
|
return default(T);
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
return default(T);
|
|
}
|
|
}
|
|
public enum CATCHUPMODE { All, OneDay, OneWeek, TwoWeeks }
|
|
///<summary>Tries to catchup (e.g. mark as read) specified feed.</summary>
|
|
public async Task<bool> CatchupFeed(int feed_id, bool is_cat, CATCHUPMODE mode = CATCHUPMODE.All)
|
|
{
|
|
assertInitialized();
|
|
|
|
var modestring = "all";
|
|
if (mode != CATCHUPMODE.All)
|
|
{
|
|
assertApiLevel(15);
|
|
switch (mode)
|
|
{
|
|
case CATCHUPMODE.OneDay:
|
|
modestring = "1day";
|
|
break;
|
|
case CATCHUPMODE.OneWeek:
|
|
modestring = "1week";
|
|
break;
|
|
case CATCHUPMODE.TwoWeeks:
|
|
modestring = "2week";
|
|
break;
|
|
}
|
|
}
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "catchupFeed",
|
|
sid = this.SessionId,
|
|
feed_id = feed_id,
|
|
is_cat = is_cat,
|
|
mode = modestring
|
|
});
|
|
var apiResponse = await get<Dictionary<string, string>>(json);
|
|
|
|
return apiResponse.ContainsKey("status") && apiResponse["status"]?.ToLower() == "ok";
|
|
}
|
|
public async Task<IEnumerable<Label>> GetLabels(int? article_id = null)
|
|
{
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "getLabels",
|
|
sid = this.SessionId,
|
|
article_id = article_id
|
|
});
|
|
var labels = await get<IEnumerable<Label>>(json);
|
|
if (this.api_level < 5)
|
|
{
|
|
foreach (var l in labels)
|
|
{
|
|
l.id = -11 - l.id;
|
|
}
|
|
}
|
|
return labels;
|
|
}
|
|
public async Task<int> SetArticleLabel(int label_id, bool assign, params int[] article_ids)
|
|
{
|
|
if (article_ids == null || article_ids.Length == 0)
|
|
{
|
|
throw new System.ArgumentNullException("ids", "need to specify at least one id");
|
|
}
|
|
assertInitialized();
|
|
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = "setArticleLabel",
|
|
sid = this.SessionId,
|
|
article_ids = string.Join(',', article_ids),
|
|
label_id = label_id,
|
|
assign = assign
|
|
});
|
|
|
|
var apiResponse = await get<Dictionary<string, string>>(json);
|
|
|
|
if (!apiResponse.ContainsKey("status") && apiResponse["status"]?.ToLower() == "ok")
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (this.api_level <= 10)
|
|
{
|
|
//update label cache
|
|
var tasks = new List<Task>();
|
|
foreach (var id in article_ids)
|
|
{
|
|
tasks.Add(GetLabels(id));
|
|
}
|
|
Task.WaitAll(tasks.ToArray());
|
|
}
|
|
int toReturn;
|
|
if (int.TryParse(apiResponse["updated"], out toReturn))
|
|
{
|
|
return toReturn;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("update ostensibly ok, but couldn't parse");
|
|
}
|
|
}
|
|
public async Task ShareToPublished(string title, Uri url, string content, bool sanitize = true)
|
|
{
|
|
assertInitialized();
|
|
assertApiLevel(4);
|
|
if (!sanitize)
|
|
assertApiLevel(20);
|
|
throw new NotImplementedException();
|
|
}
|
|
///<summary>
|
|
///<param name="login"/>if the feed requires basic HTTP auth; the login. (decidedly not self-explanatory, gdi)</param>
|
|
///<param name="password"/>if the feed requires basic HTTP auth; the password. (decidedly not self-explanatory, gdi)</param>
|
|
///</summary>
|
|
public async Task SubscribeToFeed(Uri feed_url, int category_id, string login, string password)
|
|
{
|
|
assertInitialized();
|
|
assertApiLevel(5);
|
|
throw new NotImplementedException();
|
|
}
|
|
public async Task UnsubscribeFeed()
|
|
{
|
|
assertInitialized();
|
|
assertApiLevel(5);
|
|
throw new NotImplementedException();
|
|
}
|
|
public async Task GetFeedTree()
|
|
{
|
|
assertInitialized();
|
|
assertApiLevel(5);
|
|
throw new NotImplementedException();
|
|
}
|
|
private void assertInitialized()
|
|
{
|
|
if (SessionId == null)
|
|
{
|
|
throw new InvalidOperationException("no session ID - call Login first!");
|
|
}
|
|
}
|
|
private void assertApiLevel(int ApiLevel)
|
|
{
|
|
if (ApiLevel > this.api_level)
|
|
{
|
|
throw new NotSupportedException($"method requires api level {ApiLevel}, have {this.api_level}");
|
|
}
|
|
}
|
|
|
|
|
|
private async Task<T> getOneValue<T>(string op, string key)
|
|
{
|
|
//mostly you post {"op": "getAThing", "sid": "sessionId"}
|
|
//and get back something like {"seq": 0, "status": 0, "content": {"the value you asked for": 0}}
|
|
var json = JsonContent.Create(new
|
|
{
|
|
op = op,
|
|
sid = this.SessionId
|
|
});
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ttrss.messages.ApiResponse<Dictionary<string, string>>>(response);
|
|
try
|
|
{
|
|
var converter = TypeDescriptor.GetConverter(typeof(T));
|
|
if (converter != null)
|
|
{
|
|
return (T)converter.ConvertFromString(apiResult.Content[key]);
|
|
}
|
|
return default(T);
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
return default(T);
|
|
}
|
|
}
|
|
private async Task<T> get<T>(JsonContent json)
|
|
{
|
|
var response = await (await httpClient.PostAsync(BaseURI, json)).Content.ReadAsStringAsync();
|
|
var apiResult = JsonConvert.DeserializeObject<ttrss.messages.ApiResponse<T>>(response);
|
|
return apiResult.Content;
|
|
}
|
|
}
|
|
} |