Compare commits

...

18 Commits

Author SHA1 Message Date
Starbeamrainbowlabs 849f09f666
Add EmbeddedFiles helper 2019-08-13 01:20:03 +01:00
Starbeamrainbowlabs a15f1157f0
Start on a JSON provider, but it's not finished yet 2019-08-11 16:14:25 +01:00
Starbeamrainbowlabs 6944d87726
Complete refactoring to interface-based provider discovery 2019-08-11 15:03:50 +01:00
Starbeamrainbowlabs 0afff60345
Start refactoring the html provider into it's own class, but it's not finished yet. 2019-08-10 18:56:48 +01:00
Starbeamrainbowlabs 44fe2472c3
Add IParserProvider interface.
Next up: decouple the existing HTML provider from FeedBuilder
2019-08-10 18:14:50 +01:00
Starbeamrainbowlabs 72a9f401ba
Add reflection helpers
Imported from the SVM ACW for Component-based Architectures
2019-08-10 18:05:34 +01:00
Starbeamrainbowlabs 10ce462412
[build] Use find instead of ls 2019-08-09 23:37:42 +01:00
Starbeamrainbowlabs 18b25c6dc4
Bugfix: Correctly locate .deb files for upload 2019-08-09 23:33:01 +01:00
Starbeamrainbowlabs a6ca7928c1
Add else 2019-08-09 22:05:36 +01:00
Starbeamrainbowlabs 04c63f6c09
Make GIT_TAG_NAME check conditional 2019-08-09 22:00:24 +01:00
Starbeamrainbowlabs 6ede9551d2
Dummy change to trigger rebuild 2019-08-09 21:56:55 +01:00
Starbeamrainbowlabs 75196bd277
Use new GIT_TAG_NAME and GIT_REF_TYPE laminar environment variables 2019-08-09 21:54:10 +01:00
Starbeamrainbowlabs 1040b53b3a
Update to laminar v0.6 2019-08-09 21:29:49 +01:00
Starbeamrainbowlabs 6a0dcceca5
Upload to the directory, not replace it! 2019-08-07 20:52:46 +01:00
Starbeamrainbowlabs 781d92c89f
Bugfix: Upload a specific deb file 2019-08-07 20:47:24 +01:00
Starbeamrainbowlabs 60faba6252
Let's see why it's not uploading 2019-08-07 20:40:13 +01:00
Starbeamrainbowlabs 03b3ad3c52
Bugfix: Use xargs to upload deb files one at a time 2019-08-07 20:29:30 +01:00
Starbeamrainbowlabs 72bafca0d6
Empty commit to test build system 2019-08-07 20:01:09 +01:00
12 changed files with 633 additions and 98 deletions

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
@ -10,6 +11,7 @@ using HtmlAgilityPack;
using Microsoft.SyndicationFeed;
using Microsoft.SyndicationFeed.Atom;
using PolyFeed.Helpers;
using PolyFeed.ParserProviders;
namespace PolyFeed
{
@ -49,98 +51,30 @@ namespace PolyFeed
string contentType = response.Headers.Get("content-type");
switch (source.Feed.Type) {
case SourceType.HTML:
await AddSourceHtml(source, response);
break;
default:
throw new NotImplementedException($"Error: The source type {source.Feed.Type} hasn't been implemented yet.");
}
IParserProvider provider = GetProvider(source.Feed.SourceType);
if(provider == null)
throw new ApplicationException($"Error: A provider for the source type {source.Feed.SourceType} wasn't found.");
provider.SetOutputFeed(feed, xml);
await provider.ParseWebResponse(source, response);
await Console.Error.WriteLineAsync("[Builder] Done!");
}
private async Task AddSourceHtml(FeedSource source, WebResponse response) {
await Console.Error.WriteLineAsync("[Builder/Html] Parsing Html");
private IParserProvider GetProvider(string identifier)
{
IEnumerable<Type> possibleTypes = ReflectionUtilities.IterateImplementingTypes(
typeof(IParserProvider),
Assembly.GetExecutingAssembly()
);
// Parse the HTML
HtmlDocument html = new HtmlDocument();
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
html.LoadHtml(await reader.ReadToEndAsync());
HtmlNode document = html.DocumentNode;
document.AbsolutifyUris(new Uri(source.Feed.Url));
await Console.Error.WriteLineAsync("[Builder/Html] Generating feed content");
// Add the title
await feed.WriteTitle(ReferenceSubstitutor.Replace(source.Feed.Title, document));
await feed.WriteSubtitle(ReferenceSubstitutor.Replace(source.Feed.Subtitle, document));
// Add the logo
if (source.Feed.Logo != null) {
HtmlNode logoNode = document.QuerySelector(source.Feed.Logo.Selector);
xml.WriteElementString("logo", logoNode.Attributes[source.Feed.Logo.Attribute].Value);
foreach (Type next in possibleTypes) {
IParserProvider candidate = (IParserProvider)Activator.CreateInstance(next);
if (candidate.Identifier == identifier)
return candidate;
}
// Add the feed entries
foreach (HtmlNode nextNode in document.QuerySelectorAll(source.Entries.Selector)) {
HtmlNode urlNode = nextNode.QuerySelector(source.Entries.Url.Selector);
if (urlNode == null)
throw new ApplicationException("Error: Failed to match entry url selector against an element.");
string url = source.Entries.Url.Attribute == string.Empty ?
urlNode.InnerText : urlNode.Attributes[source.Entries.Url.Attribute].DeEntitizeValue;
Uri entryUri = new Uri(new Uri(source.Feed.Url), new Uri(url));
AtomEntry nextItem = new AtomEntry() {
Id = entryUri.ToString(),
Title = ReferenceSubstitutor.Replace(source.Entries.Title, nextNode),
Description = ReferenceSubstitutor.Replace(source.Entries.Content, nextNode),
ContentType = "html"
};
nextItem.AddLink(new SyndicationLink(entryUri, AtomLinkTypes.Alternate));
if (source.Entries.Published != null) {
nextItem.Published = DateTime.Parse(
nextNode.QuerySelectorAttributeOrText(
source.Entries.Published
)
);
}
if (source.Entries.LastUpdated != null) {
nextItem.LastUpdated = DateTime.Parse(
nextNode.QuerySelectorAttributeOrText(
source.Entries.LastUpdated
)
);
}
else if (source.Entries.Published != null) // Use the publish date if available
nextItem.LastUpdated = nextItem.Published;
else // It requires one, apparently
nextItem.LastUpdated = DateTimeOffset.Now;
if (source.Entries.AuthorName != null) {
SyndicationPerson author = new SyndicationPerson(
nextNode.QuerySelectorAttributeOrText(source.Entries.AuthorName).Trim(),
""
);
if (source.Entries.AuthorUrl != null)
author.Uri = nextNode.QuerySelectorAttributeOrText(source.Entries.AuthorUrl);
nextItem.AddContributor(author);
}
else
nextItem.AddContributor(new SyndicationPerson("Unknown", ""));
await feed.Write(nextItem);
}
return null;
}
public async Task<string> Render()

View File

@ -37,7 +37,6 @@ namespace PolyFeed
/// The type of source document to expect.
/// </summary>
public string SourceType { get; set; }
public SourceType Type => (SourceType)Enum.Parse(typeof(SourceType), SourceType, true);
/// <summary>

View File

@ -0,0 +1,287 @@
using System;
using System.Reflection;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace SBRL.Utilities
{
/// <summary>
/// A collection of static methods for manipulating embedded resources.
/// </summary>
/// <description>
/// From https://gist.github.com/sbrl/aabfcfe87396b8c05d3263887b807d23. You may have seen
/// this in several other ACWs I've done. Proof I wrote this is available upon request,
/// of course.
///
/// v0.6.2, by Starbeamrainbowlabs <feedback@starbeamrainbowlabs.com>
/// Last updated 28th November 2018.
/// Licensed under MPL-2.0.
///
/// Changelog:
/// v0.1 (25th July 2016):
/// - Initial release.
/// v0.2 (8th August 2016):
/// - Changed namespace.
/// v0.3 (21st January 2017):
/// - Added GetRawReader().
/// v0.4 (8th April 2017):
/// - Removed unnecessary using statement.
/// v0.5 (3rd September 2017):
/// - Changed namespace
/// v0.6 (12th October 2018):
/// - Fixed assembly / calling assembly bugs
/// v0.6.1 (17th october 2018):
/// - Fix crash in ReadAllText(filename)
/// v0.6.2 (28th November 2018):
/// - Fix assembly targeting bug in ReadAllBytesAsync()
/// </description>
public static class EmbeddedFiles
{
/// <summary>
/// An array of the filenames of all the resources embedded in the target assembly.
/// </summary>
/// <param name="targetAssembly">The target assembly to extract a resource list for.</param>
/// <value>The resource list.</value>
public static string[] ResourceList(Assembly targetAssembly)
{
return targetAssembly.GetManifestResourceNames();
}
/// <summary>
/// An array of the filenames of all the resources embedded in the calling assembly.
/// </summary>
/// <value>The resource list.</value>
public static string[] ResourceList()
{
return ResourceList(Assembly.GetCallingAssembly());
}
/// <summary>
/// Gets a list of resources embedded in the calling assembly as a string.
/// </summary>
public static string GetResourceListText()
{
return GetResourceListText(Assembly.GetCallingAssembly());
}
/// <summary>
/// Gets a list of resources embedded in the target assembly as a string.
/// </summary>
/// <param name="targetAssembly">The target assembly to extract a resource list from.</param>
public static string GetResourceListText(Assembly targetAssembly)
{
StringWriter result = new StringWriter();
result.WriteLine("Files embedded in {0}:", targetAssembly.GetName().Name);
foreach (string filename in ResourceList(targetAssembly))
result.WriteLine(" - {0}", filename);
return result.ToString();
}
/// <summary>
/// Writes a list of resources embedded in the calling assembly to the standard output.
/// </summary>
public static void WriteResourceList()
{
Console.WriteLine(GetResourceListText(Assembly.GetCallingAssembly()));
}
/// <summary>
/// Gets a StreamReader attached to the specified embedded resource.
/// </summary>
/// <param name="filename">The filename of the embedded resource to get a StreamReader of.</param>
/// <returns>A StreamReader attached to the specified embedded resource.</returns>
public static StreamReader GetReader(string filename)
{
return new StreamReader(GetRawReader(filename));
}
/// <summary>
/// Gets a raw Stream that's attached to the specified embedded resource
/// in the calling assembly.
/// Useful when you want to copy an embedded resource to some other stream.
/// </summary>
/// <param name="filename">The path to the embedded resource.</param>
/// <returns>A raw Stream object attached to the specified file.</returns>
public static Stream GetRawReader(string filename)
{
return GetRawReader(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Gets a raw Stream that's attached to the specified embedded resource
/// in the specified assembly.
/// Useful when you want to copy an embedded resource to some other stream.
/// </summary>
/// <param name="targetAssembly">The assembly to search for the filename in.</param>
/// <param name="filename">The path to the embedded resource.</param>
/// <returns>A raw Stream object attached to the specified file.</returns>
public static Stream GetRawReader(Assembly targetAssembly, string filename)
{
return targetAssembly.GetManifestResourceStream(filename);
}
/// <summary>
/// Gets the specified embedded resource's content as a byte array.
/// </summary>
/// <param name="filename">The filename of the embedded resource to get conteent of.</param>
/// <returns>The specified embedded resource's content as a byte array.</returns>
public static byte[] ReadAllBytes(string filename)
{
// Referencing the Result property will block until the async method completes
return ReadAllBytesAsync(filename).Result;
}
/// <summary>
/// Gets the content of the resource that's embedded in the specified
/// assembly as a byte array asynchronously.
/// </summary>
/// <param name="targetAssembly">The assembly to search for the file in.</param>
/// <param name="filename">The filename of the embedded resource to get content of.</param>
/// <returns>The specified embedded resource's content as a byte array.</returns>
public static async Task<byte[]> ReadAllBytesAsync(Assembly targetAssembly, string filename)
{
using (Stream resourceStream = targetAssembly.GetManifestResourceStream(filename))
using (MemoryStream temp = new MemoryStream())
{
await resourceStream.CopyToAsync(temp);
return temp.ToArray();
}
}
public static async Task<byte[]> ReadAllBytesAsync(string filename)
{
return await ReadAllBytesAsync(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Gets all the text stored in the resource that's embedded in the
/// calling assembly.
/// </summary>
/// <param name="filename">The filename to fetch the content of.</param>
/// <returns>All the text stored in the specified embedded resource.</returns>
public static string ReadAllText(string filename)
{
return ReadAllTextAsync(Assembly.GetCallingAssembly(), filename).Result;
}
/// <summary>
/// Gets all the text stored in the resource that's embedded in the
/// specified assembly.
/// </summary>
/// <param name="targetAssembly">The assembly from in which to look for the target embedded resource.</param>
/// <param name="filename">The filename to fetch the content of.</param>
/// <returns>All the text stored in the specified embedded resource.</returns>
public static string ReadAllText(Assembly targetAssembly, string filename)
{
return ReadAllTextAsync(targetAssembly, filename).Result;
}
/// <summary>
/// Gets all the text stored in the resource that's embedded in the
/// specified assembly asynchronously.
/// </summary>
/// <param name="filename">The filename to fetch the content of.</param>
/// <returns>All the text stored in the specified embedded resource.</returns>
public static async Task<string> ReadAllTextAsync(Assembly targetAssembly, string filename)
{
using (StreamReader resourceReader = new StreamReader(targetAssembly.GetManifestResourceStream(filename)))
{
return await resourceReader.ReadToEndAsync();
}
}
/// <summary>
/// Gets all the text stored in the resource that's embedded in the
/// calling assembly asynchronously.
/// </summary>
/// <param name="filename">The filename to fetch the content of.</param>
/// <returns>All the text stored in the specified embedded resource.</returns>
public static async Task<string> ReadAllTextAsync(string filename)
{
return await ReadAllTextAsync(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Enumerates the lines of text in the embedded resource that's
/// embedded in the calling assembly.
/// </summary>
/// <param name="filename">The filename of the embedded resource to enumerate.</param>
/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
public static IEnumerable<string> EnumerateLines(string filename)
{
return EnumerateLines(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Enumerates the lines of text in the embedded resource that's
/// embedded in the specified assembly.
/// </summary>
/// <param name="filename">The filename of the embedded resource to enumerate.</param>
/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
public static IEnumerable<string> EnumerateLines(Assembly targetAssembly, string filename)
{
foreach (Task<string> nextLine in EnumerateLinesAsync(targetAssembly, filename))
yield return nextLine.Result;
}
/// <summary>
/// Enumerates the lines of text in the resource that's embedded in the
/// specified assembly asynchronously.
/// Each successive call returns a task that, when complete, returns
/// the next line of text stored in the embedded resource.
/// </summary>
/// <param name="targetAssembly">The target assembly in which to look for the embedded resource.</param>
/// <param name="filename">The filename of the embedded resource to enumerate.</param>
/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
public static IEnumerable<Task<string>> EnumerateLinesAsync(Assembly targetAssembly, string filename)
{
using (StreamReader resourceReader = new StreamReader(targetAssembly.GetManifestResourceStream(filename)))
{
while (!resourceReader.EndOfStream)
yield return resourceReader.ReadLineAsync();
}
}
/// <summary>
/// Enumerates the lines of text in the resource that's embedded in the
/// calling assembly asynchronously.
/// Each successive call returns a task that, when complete, returns
/// the next line of text stored in the embedded resource.
/// </summary>
/// <param name="filename">The filename of the embedded resource to enumerate.</param>
/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
public static IEnumerable<Task<string>> EnumerateLinesAsync(string filename)
{
return EnumerateLinesAsync(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Gets all the lines of text in the specified embedded resource.
/// You might find EnumerateLines(string filename) more useful depending on your situation.
/// </summary>
/// <param name="filename">The filename to obtain the lines of text from.</param>
/// <returns>A list of lines in the specified embedded resource.</returns>
public static List<string> GetAllLines(string filename)
{
// Referencing the Result property will block until the async method completes
return GetAllLinesAsync(filename).Result;
}
/// <summary>
/// Gets all the lines of text in the resource that's embedded in the
/// calling assembly asynchronously.
/// </summary>
/// <param name="filename">The filename to obtain the lines of text from.</param>
/// <returns>A list of lines in the specified embedded resource.</returns>
public static async Task<List<string>> GetAllLinesAsync(string filename)
{
return await GetAllLinesAsync(Assembly.GetCallingAssembly(), filename);
}
/// <summary>
/// Gets all the lines of text in the resource that's embedded in the
/// specified assembly asynchronously.
/// </summary>
/// <param name="filename">The filename to obtain the lines of text from.</param>
/// <returns>A list of lines in the specified embedded resource.</returns>
public static async Task<List<string>> GetAllLinesAsync(Assembly targetAssembly, string filename)
{
List<string> result = new List<string>();
foreach (Task<string> nextLine in EnumerateLinesAsync(targetAssembly, filename))
result.Add(await nextLine);
return result;
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.IO;
namespace PolyFeed.Helpers
{
internal static class ReflectionUtilities
{
public static bool Verbose = true;
public static IEnumerable<Assembly> IterateLoadedAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
public static IEnumerable<Type> IterateAllLoadedTypes()
{
return IterateLoadedAssemblies()
.SelectMany((Assembly nextAssembly) => nextAssembly.GetTypes());
}
public static IEnumerable<Type> IterateLoadedTypes(Assembly targetAssembly)
{
return targetAssembly.GetTypes();
}
public static void LoadAssembliesDirectory(string directory)
{
foreach (string nextDll in Directory.EnumerateFiles(directory, "*.dll"))
{
AssemblyName assemblyName = AssemblyName.GetAssemblyName(nextDll);
// Check that a strongname is actually present
if (assemblyName.GetPublicKeyToken().Length < 8)
continue;
// Verify the strongname is valid.
/*try
{
bool isOk = false;
NativeMethods.StrongNameSignatureVerificationEx(nextDll, 0xFF, ref isOk);
if (!isOk)
continue;
}
catch (DllNotFoundException error)
{
Console.Error.WriteLine($"Warning: Unable to verify the integrity of the StrongName for '{nextDll}' (DLL not found: {error.Message}).");
}*/
try
{
Assembly.LoadFrom(nextDll);
// FUTURE: Consider using Assembly.ReflectionOnlyLoadFrom in a separate AppDomain to figure out if there's anything useful in an assembly before loading it for reals
}
catch (BadImageFormatException error)
{
if (Verbose) Console.Error.WriteLine($"Error loading '{nextDll}': {error.Message}");
}
}
}
/// <summary>
/// Searches the types present in the specified assembly to find a type that implements
/// the specified interface.
/// </summary>
/// <param name="targetInterface">The target interface that returned types should implement.</param>
/// <param name="assemblyToSearch">The assembly to search through for matching types.</param>
public static IEnumerable<Type> IterateImplementingTypes(Type targetInterface, Assembly assemblyToSearch)
{
if (!targetInterface.IsInterface)
throw new ArgumentException($"Error: The specified type {targetInterface} is not an " +
"interface, so it can't be used to search for implementing types.");
// FUTURE: Add caching here? Reflection is slow
foreach (Type nextType in IterateAllLoadedTypes())
{
// Interfaces implement themselves, but we don't want to return the interface itself
if (nextType == targetInterface)
continue;
// Make sure it implements the specified interface
if (!targetInterface.IsAssignableFrom(nextType))
continue;
yield return nextType;
}
}
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Xml;
using Fizzler.Systems.HtmlAgilityPack;
using HtmlAgilityPack;
using Microsoft.SyndicationFeed;
using Microsoft.SyndicationFeed.Atom;
using PolyFeed.Helpers;
namespace PolyFeed.ParserProviders
{
public class HtmlParserProvider : IParserProvider
{
private XmlWriter xml = null;
private AtomFeedWriter feed = null;
public string Identifier => "html";
public HtmlParserProvider()
{
}
public void SetOutputFeed(AtomFeedWriter inFeed, XmlWriter inXml) {
xml = inXml;
feed = inFeed;
}
public async Task ParseWebResponse(FeedSource source, WebResponse response)
{
await Console.Error.WriteLineAsync("[Builder/Html] Parsing Html");
// Parse the HTML
HtmlDocument html = new HtmlDocument();
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
html.LoadHtml(await reader.ReadToEndAsync());
HtmlNode document = html.DocumentNode;
document.AbsolutifyUris(new Uri(source.Feed.Url));
await Console.Error.WriteLineAsync("[Builder/Html] Generating feed content");
// Add the title
await feed.WriteTitle(ReferenceSubstitutor.Replace(source.Feed.Title, document));
await feed.WriteSubtitle(ReferenceSubstitutor.Replace(source.Feed.Subtitle, document));
// Add the logo
if (source.Feed.Logo != null) {
HtmlNode logoNode = document.QuerySelector(source.Feed.Logo.Selector);
xml.WriteElementString("logo", logoNode.Attributes[source.Feed.Logo.Attribute].Value);
}
// Add the feed entries
foreach (HtmlNode nextNode in document.QuerySelectorAll(source.Entries.Selector))
{
await addEntry(source, nextNode);
}
}
private async Task addEntry(FeedSource source, HtmlNode nextNode)
{
HtmlNode urlNode = nextNode.QuerySelector(source.Entries.Url.Selector);
if (urlNode == null)
throw new ApplicationException("Error: Failed to match entry url selector against an element.");
string url = source.Entries.Url.Attribute == string.Empty ?
urlNode.InnerText : urlNode.Attributes[source.Entries.Url.Attribute].DeEntitizeValue;
Uri entryUri = new Uri(new Uri(source.Feed.Url), new Uri(url));
AtomEntry nextItem = new AtomEntry() {
Id = entryUri.ToString(),
Title = ReferenceSubstitutor.Replace(source.Entries.Title, nextNode),
Description = ReferenceSubstitutor.Replace(source.Entries.Content, nextNode),
ContentType = "html"
};
nextItem.AddLink(new SyndicationLink(entryUri, AtomLinkTypes.Alternate));
if (source.Entries.Published != null) {
nextItem.Published = DateTime.Parse(
nextNode.QuerySelectorAttributeOrText(
source.Entries.Published
)
);
}
if (source.Entries.LastUpdated != null) {
nextItem.LastUpdated = DateTime.Parse(
nextNode.QuerySelectorAttributeOrText(
source.Entries.LastUpdated
)
);
}
else if (source.Entries.Published != null) // Use the publish date if available
nextItem.LastUpdated = nextItem.Published;
else // It requires one, apparently
nextItem.LastUpdated = DateTimeOffset.Now;
if (source.Entries.AuthorName != null) {
SyndicationPerson author = new SyndicationPerson(
nextNode.QuerySelectorAttributeOrText(source.Entries.AuthorName).Trim(),
""
);
if (source.Entries.AuthorUrl != null)
author.Uri = nextNode.QuerySelectorAttributeOrText(source.Entries.AuthorUrl);
nextItem.AddContributor(author);
}
else
nextItem.AddContributor(new SyndicationPerson("Unknown", ""));
await feed.Write(nextItem);
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Net;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.SyndicationFeed.Atom;
namespace PolyFeed.ParserProviders
{
/// <summary>
/// Defines the functionality that a source parser should provide.
/// Sources are represented by a <see cref="FeedSource" /> object, and source parsers
/// are responsible for parsing it and populating a given atom feed.
/// </summary>
public interface IParserProvider
{
/// <summary>
/// The identifier of this provider.
/// Used in the .toml configuration file to specify which parser to use.
/// </summary>
string Identifier { get; }
/// <summary>
/// Sets the output feed that parsed output should be written to.
/// </summary>
/// <param name="feed">The output feed writer that output should be written to.</param>
/// <param name="xml">The underlying XML feed try not to use this unless you *really* have to.</param>
void SetOutputFeed(AtomFeedWriter feed, XmlWriter xml);
/// <summary>
/// Parses a web response that's paired with a given <see cref="FeedSource" />.
/// </summary>
/// <param name="source">The <see cref="FeedSource"/> object that the <paramref name="response"/> was generated from.</param>
/// <param name="response">The <see cref="WebResponse"/> in question needs parsing.</param>
Task ParseWebResponse(FeedSource source, WebResponse response);
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Net;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.SyndicationFeed.Atom;
using System.Json;
using System.IO;
namespace PolyFeed.ParserProviders
{
public class JsonParserProvider : IParserProvider
{
private XmlWriter xml = null;
private AtomFeedWriter feed = null;
public string Identifier => "json";
public JsonParserProvider() {
}
public void SetOutputFeed(AtomFeedWriter inFeed, XmlWriter inXml) {
xml = inXml;
feed = inFeed;
}
public async Task ParseWebResponse(FeedSource source, WebResponse response)
{
string jsonText;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
jsonText = await reader.ReadToEndAsync();
JsonValue jsonValue = JsonValue.Parse(jsonText);
JsonObject json = jsonValue as JsonObject;
if (json == null)
throw new ApplicationException("Error: Failed to parse the JSON into an object.");
throw new NotImplementedException("Error 501: Not implemented :-/");
}
}
}

View File

@ -126,15 +126,13 @@
<Reference Include="Fizzler.Systems.HtmlAgilityPack">
<HintPath>..\packages\Fizzler.Systems.HtmlAgilityPack.1.2.0\lib\netstandard1.3\Fizzler.Systems.HtmlAgilityPack.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Microsoft.SyndicationFeed.ReaderWriter">
<HintPath>..\packages\Microsoft.SyndicationFeed.ReaderWriter.1.0.2\lib\netstandard1.3\Microsoft.SyndicationFeed.ReaderWriter.dll</HintPath>
</Reference>
<Reference Include="Nett">
<HintPath>..\packages\Nett.0.13.0\lib\net40\Nett.dll</HintPath>
</Reference>
<Reference Include="System.Json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
@ -151,6 +149,11 @@
<Compile Include="SnakeCasePropertySelector.cs" />
<Compile Include="Helpers\HtmlHelpers.cs" />
<Compile Include="Helpers\UserAgentHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="ParserProviders\IParserProvider.cs" />
<Compile Include="ParserProviders\HtmlParserProvider.cs" />
<Compile Include="ParserProviders\JsonParserProvider.cs" />
<Compile Include="Helpers\EmbeddedFiles.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -158,6 +161,7 @@
<ItemGroup>
<Folder Include="Salamander.Core\" />
<Folder Include="Helpers\" />
<Folder Include="ParserProviders\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" />

View File

@ -17,7 +17,7 @@ using System.Runtime.CompilerServices;
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.1.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.

View File

@ -8,7 +8,6 @@
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net47" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net47" />
<package id="Nett" version="0.13.0" targetFramework="net47" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net47" />
<package id="System.AppContext" version="4.3.0" targetFramework="net47" />
<package id="System.Collections" version="4.3.0" targetFramework="net47" />
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net47" />

31
build
View File

@ -91,7 +91,7 @@ task_package() {
PolyFeed/bin/polyfeed=/usr/lib \
polyfeed=/usr/bin/polyfeed;
execute rm -r PolyFeed/bin/polyfeed;
execute dpkg -c *.deb; # We don't know it's name :P
execute dpkg -c "$(ls -ct *.deb | head -n1)"; # We don't know it's name :P
task_end $?;
}
@ -102,23 +102,42 @@ task_archive() {
task_end $?;
}
task_upload-release() {
task_begin "Uploading release .deb file";
# $1 - The filename to upload
_upload_deb() {
filename="${1}";
echo "Found ${filename}";
sftp -i "${SSH_KEY_PATH}" -P "${deploy_ssh_port}" -o PasswordAuthentication=no "${deploy_ssh_user}@${deploy_ssh_host}" << SFTPCOMMANDS
put ./*.deb ${deploy_root_dir}
put ${filename} ${deploy_root_dir}/
bye
SFTPCOMMANDS
}
task_upload-release() {
task_begin "Uploading release .deb file(s)";
# We grab from ARCHIVE here, as we expect that only archived .deb files are final & stable.
_upload_deb "$(find "${ARCHIVE}" -name "*.deb" | head -n1)";
task_end $?;
}
task_ci() {
task_begin "Checking environment";
check_env_variable "ARCHIVE";
check_env_variable "GIT_REF_NAME";
check_env_variable "GIT_REF_TYPE";
[[ "${GIT_REF_TYPE}" == "tags" ]] && check_env_variable "GIT_TAG_NAME";
task_end 0;
tasks_run setup build package archive;
if [[ "$(git tag --points-at HEAD | wc -l)" -gt 0 ]]; then
echo "Found tag $(git tag --points-at HEAD), uploading release";
if [[ "${GIT_REF_TYPE}" == "tags" ]]; then
echo "Found tag ${GIT_TAG_NAME}, uploading release";
tasks_run upload-release;
else
echo "No tags found, skipping uploading release";
fi
}

@ -1 +1 @@
Subproject commit 617fcdb5b9df7f57ccb6639a87be68238f99b9ed
Subproject commit 5b27397f5f1c21a447bb56be6bffbfbd9290c3e3