Skip to content

Commit

Permalink
Add project files.
Browse files Browse the repository at this point in the history
  • Loading branch information
liebki committed May 13, 2023
1 parent 222835e commit 9c79e40
Show file tree
Hide file tree
Showing 12 changed files with 1,831 additions and 0 deletions.
611 changes: 611 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions DISCLAIMER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# DISCLAIMER

### Definitions

**Service** refers to the GithubNet library.

### Disclaimer

The information contained in the Service is for general information purposes only.

I am not responsible for any errors or omissions in the contents of the Service.

In no event shall I be liable for any special, direct, indirect, consequential, or incidental damages or any damages whatsoever, whether in an action of contract, negligence or other tort, arising out of or in connection with the use of the Service or the contents of the Service. I reserve the right to make additions, deletions, or modifications to the contents of the Service at any time without prior notice.

### External Links Disclaimer

The Service may contain links to external websites that are not provided or maintained by or in any way affiliated with me.

Please note that I do not guarantee the accuracy, relevance, timeliness, or completeness of any information on these external websites.

### Errors and Omissions Disclaimer

The information provided by the Service is for general guidance on matters of interest only. Even if I take every precaution to ensure that the content of the Service is both current and accurate, errors can occur. Plus, given the changing nature of technologies, there may be delays, omissions, or inaccuracies in the information contained in the Service.

I am not responsible for any errors or omissions, or for the results obtained from the use of this information.

### No Responsibility Disclaimer

The information in the Service is provided with the understanding that I am not engaged in rendering legal, accounting, tax, or other professional advice and services. As such, it should not be used as a substitute for consultation with professional accounting, tax, legal, or other competent advisers.

In no event shall I be liable for any special, incidental, indirect, or consequential damages whatsoever arising out of or in connection with your access or use or inability to access or use the Service.

### "Use at Your Own Risk" Disclaimer

All information in the Service is provided "as is," with no guarantee of completeness, accuracy, timeliness, or of the results obtained from the use of this information, and without warranty of any kind, express or implied, including, but not limited to warranties of performance, merchantability, and fitness for a particular purpose.

I will not be liable to You or anyone else for any decision made or action taken in reliance on the information provided by the Service or for any consequential, special, or similar damages, even if advised of the possibility of such damages.

### Contact Us

If you have any questions about this Disclaimer, you can contact me:

By mail: kxmliebl#at#gmail#com
36 changes: 36 additions & 0 deletions GithubNet.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GithubNetDemo", "GithubNetDemo\GithubNetDemo.csproj", "{3CDBD8B6-80CC-44BC-B4D4-7BBE7A1F62F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GithubNet", "GithubNet\GithubNet.csproj", "{F190FF77-2F1D-42F3-A3C9-07214616BFFC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{46C0F91F-23FE-4E43-9F4B-484D96B18086}"
ProjectSection(SolutionItems) = preProject
LICENSE.txt = LICENSE.txt
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3CDBD8B6-80CC-44BC-B4D4-7BBE7A1F62F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CDBD8B6-80CC-44BC-B4D4-7BBE7A1F62F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CDBD8B6-80CC-44BC-B4D4-7BBE7A1F62F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CDBD8B6-80CC-44BC-B4D4-7BBE7A1F62F0}.Release|Any CPU.Build.0 = Release|Any CPU
{F190FF77-2F1D-42F3-A3C9-07214616BFFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F190FF77-2F1D-42F3-A3C9-07214616BFFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F190FF77-2F1D-42F3-A3C9-07214616BFFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F190FF77-2F1D-42F3-A3C9-07214616BFFC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21AEC107-E182-405B-A29D-2B1CE629AF7A}
EndGlobalSection
EndGlobal
43 changes: 43 additions & 0 deletions GithubNet/GithubNet.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>GithubNet</Title>
<Version>0.1</Version>
<Authors>liebki</Authors>
<Description>This library allows you to retrieve trending GitHub repositories and their information.</Description>
<Copyright>liebki</Copyright>
<PackageProjectUrl>github.com/liebki/githubnet</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>github.com/liebki/githubnet</RepositoryUrl>
<PackageTags>github, crawler, github-trends, crawling, microsoft, git</PackageTags>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageIcon>icon.png</PackageIcon>
<PackageId>GithubDataNet</PackageId>
</PropertyGroup>

<ItemGroup>
<None Include="..\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\LICENSE.txt">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="CssSelectors.Core.HtmlAgilityPack" Version="1.0.4" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions GithubNet/GithubNetClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace GithubNet
{
public class GithubNetClient
{
public async Task<List<TrendItem>> GetTrendItemsAsync(bool loadTrendItemDetails = false, string customQuery = "https://github.com/trending")
{
if (!string.IsNullOrEmpty(customQuery) && customQuery.Contains("https://github.com/trending", StringComparison.InvariantCultureIgnoreCase))
{
return await GithubNetManager.GetAllTrendEntries(loadTrendItemDetails, customQuery);
}
else
{
throw new ArgumentException("The parameter customquery, can't be whitespace, null or empty also it has to contain some kind of https://github.com/trending-url string!");
}
}

public async Task<TrendItem> GetTrendItemDetailsAsync(TrendItem entryItem)
{
if (entryItem != null && !string.IsNullOrEmpty(entryItem.RespositoryLink) && !string.IsNullOrWhiteSpace(entryItem.RespositoryLink))
{
return await GithubNetManager.GetTrendDetails(entryItem);
}
else
{
throw new ArgumentException("The parameter entryItem, can't be null also the RespositoryLink inside entryItem, has to contain a value and may not be null or empty!");
}
}

public string GetTopicUrlFromTopicName(string topicName)
{
if (!string.IsNullOrEmpty(topicName) && !string.IsNullOrWhiteSpace(topicName))
{
return GithubNetManager.ParseTopicNameToUrl(topicName);
}
else
{
throw new ArgumentException("The parameter topicName, can't be whitespace, null or empty, it has to contain some kind of value!");
}
}
}
}
185 changes: 185 additions & 0 deletions GithubNet/GithubNetManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using HtmlAgilityPack;
using System.Net;
using System.Text;

namespace GithubNet
{
internal static class GithubNetManager
{
internal static async Task<List<TrendItem>> GetAllTrendEntries(bool loadTrendItemDetails = false, string customQuery = "https://github.com/trending")
{
List<TrendItem> TrendingRepositoriesList = new();
HtmlWeb Github = new();

HtmlDocument GithubDoc = Github.Load(customQuery);
IEnumerable<HtmlNode> nodes = GithubDoc.QuerySelectorAll("article.Box-row");

foreach (HtmlNode productElement in nodes)
{
try
{
HtmlNode Description = productElement.QuerySelector("article.Box-row > p");
HtmlNode Username = productElement.QuerySelector("article.Box-row span.text-normal");

HtmlNode RepositoryLink = productElement.QuerySelector("article.Box-row > h2 > a");
IList<HtmlNode> TotalForksAndStars = productElement.QuerySelectorAll("article.Box-row > div > a.Link--muted");

string DescriptionFiltered = string.Empty;
if (Description != null)
{
DescriptionFiltered = FilterLineBreaks(Description.InnerText);
DescriptionFiltered = WebUtility.HtmlDecode(DescriptionFiltered);
}
string UsernameFiltered = FilterLineBreaks(Username.InnerText.Replace(" /", string.Empty));

string RepositoryLinkFiltered = $"https://github.com{RepositoryLink.Attributes["href"].Value}";
string[] RepositoryNameParse = RepositoryLinkFiltered.Split('/');

string RepositoryName = "An error occured";

if (RepositoryNameParse?.Length >= 2)
{
RepositoryName = RepositoryNameParse[4];
}

string TotalStarsFiltered = FilterLineBreaks(TotalForksAndStars[0].InnerText);
string TotalForksFiltered = FilterLineBreaks(TotalForksAndStars[1].InnerText);

string ProgrammingLanguage = "None";
if (productElement.QuerySelectorAll("article.Box-row > div > span > span") != null)
{
IList<HtmlNode> ProgrammingLanguageElementCount = productElement.QuerySelectorAll("article.Box-row > div > span > span");
if (ProgrammingLanguageElementCount.Count > 0)
{
ProgrammingLanguage = ProgrammingLanguageElementCount[1].InnerText;
}
}

if (loadTrendItemDetails)
{
TrendItem trendItem = new(UsernameFiltered, RepositoryLinkFiltered, RepositoryName, DescriptionFiltered, TotalStarsFiltered, TotalForksFiltered, ProgrammingLanguage);
TrendingRepositoriesList.Add(await GetTrendDetails(trendItem));
}
else
{
TrendingRepositoriesList.Add(new(UsernameFiltered, RepositoryLinkFiltered, RepositoryName, DescriptionFiltered, TotalStarsFiltered, TotalForksFiltered, ProgrammingLanguage));
}
}
catch (Exception)
{
//Skip TrendItem if corrupt
}
}

return TrendingRepositoriesList;
}

internal static async Task<TrendItem> GetTrendDetails(TrendItem entryItem)
{
entryItem.HasDetails = true;
HtmlWeb TrendingRepository = new();

HtmlDocument TrendingRep = TrendingRepository.Load(entryItem.RespositoryLink);
HtmlNode ArchiveStatus = TrendingRep.DocumentNode.SelectSingleNode("/html/body/div[1]/div[4]/div/main/div[1]");

HtmlNode ProjectUrl = TrendingRep.QuerySelector("div.my-3:nth-child(3) > span:nth-child(2) > a:nth-child(1)");
IList<HtmlNode> Topics = TrendingRep.QuerySelectorAll("a.topic-tag");

if (ArchiveStatus?.InnerHtml.Contains("This repository has been archived by the owner on", StringComparison.InvariantCultureIgnoreCase) == true)
{
entryItem.IsArchived = true;
}

if (ProjectUrl != null)
{
entryItem.HasProjectUrl = true;
entryItem.ProjectUrl = ProjectUrl.Attributes["href"].Value;
}

if (Topics.Count > 0)
{
List<string> TopicNames = new();
foreach (HtmlNode TopicItem in Topics)
{
TopicNames.Add(FilterLineBreaks(TopicItem.InnerHtml));
}
entryItem.Topics = TopicNames.ToArray();
entryItem.HasTopics = true;
}
else
{
entryItem.Topics = Array.Empty<string>();
}

entryItem.LastCommitTime = $"Last commit: {GetLastCommitTime(entryItem)}";
return entryItem;
}

private static string GetLastCommitTime(TrendItem entry)
{
string LastCommitTime = "Error";
string CommitTime = FetchLastCommitTime(entry, "master");

if (string.IsNullOrEmpty(CommitTime))
{
CommitTime = FetchLastCommitTime(entry, "main");
if (!string.IsNullOrEmpty(CommitTime))
{
entry.LastCommitUrl = entry.GetLastCommitUrl("main");
LastCommitTime = CommitTime;
}
}
else
{
entry.LastCommitUrl = entry.GetLastCommitUrl("master");
LastCommitTime = CommitTime;
}

return LastCommitTime;
}

private static string FetchLastCommitTime(TrendItem entry, string codeBranch)
{
HtmlWeb CommitUrl = new();
HtmlDocument TrendingRep = CommitUrl.Load(entry.GetLastCommitUrl(codeBranch));

HtmlNode LastCommitTimeRaw = TrendingRep.QuerySelector("relative-time.no-wrap");

if (LastCommitTimeRaw?.InnerHtml.Length > 0)
{
return FilterLineBreaks(LastCommitTimeRaw.InnerHtml);
}
return null;
}

private static string FilterLineBreaks(string textIn)
{
StringBuilder sb = new(textIn.Length);
foreach (char i in textIn)
{
if (i != '\n' && i != '\r' && i != '\t')
{
sb.Append(i);
}
}
return sb.ToString().Trim();
}

public static string ParseTopicNameToUrl(string topicNameIn)
{
string LanguageName = NormalizeLanguageNameIdentifier(topicNameIn);
return $"https://github.com/topics/{LanguageName}";
}

private static string NormalizeLanguageNameIdentifier(string topicNameIn)
{
string normalized = topicNameIn.Replace(" ", "-").ToLowerInvariant();
return normalized switch
{
"c#" => "csharp",
"c++" => "cpp",
_ => normalized,
};
}
}
}
Loading

0 comments on commit 9c79e40

Please sign in to comment.