-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,831 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
} | ||
} | ||
} |
Oops, something went wrong.